I have this Table of Contents class below that will find all <h2>
<h3>
and <h4>
tags in a wordpress post and it will create a Table of Contents out of them, it adds an ID to each heading so when you click a link it will take you to that part of the page.
Here is a snapshot of what the generated TOC can look like…
Now the problem I have with this class it is simply finds all Headings and builds the TOC on page load, it then prepends the generated TOC to the beginning of the $content
post.
I would like to figure out a way where I can specify where the TOC will be shown in the post. So for example if a post has a featured image, then I would like to show the TOC below the image instead of at the very beginning of the post. Something like a shortcode
would be ideal for this but I am not sure how to get it to work with a shortcode, I tried creating a shortcode which would run this class but it doesn’t work as I am wanting it to, it doesn’t create the TOC when I run it with a shortcode.
Anyone have any ideas how to improve this and get it working as I describe?
The class is ran with this Filter
add_filter('the_content', array('TableOfContents', 'writeTOC'));
The class
/**
* cd-table-of-contents.php
*/
class TableOfContents {
/**
* Counts the occurence of header elements in WordPress content
*
* @param type $content
* @return null|boolean|array
*/
static function hasToc($tiers, $content) {
$pattern = '/<h[2-' . $tiers . ']*[^>]*>(.*?)</h([2-' . $tiers . '])>/';
$return = array();
if (empty($content))
return null;
if (!preg_match_all($pattern, $content, $return)) {
return false;
}
return $return;
}
/**
* Generates a table of content only when singular pages are being viewed
*
* @param type $tiers
* @param type $text
*/
static function generateTableOfContents($tiers, $content, $draw = TRUE, $return = array()) {
if (!is_singular())
return $content;
// numbers on or off?
$num_on = true;
$content = $toc . $content;
$searches = array();
$replaces = array();
$return = (is_array($return) && !empty($return) ) ? $return : TableOfContents::hasToc($tiers, $content);
if ($draw && !empty($return)):
if($num_on){
$toc = '<div class="toc nonumbers">';
}else{
$toc = '<div class="toc">';
}
$toc .= "<h4>Table of Contents</h4>";
$toc .= "<ul class="parent start">";
$tags = reset($return);
$titles = $return[1];
$levels = end($return);
$_level = 2;
$chapters = array('0','0','0','0','0','0');
$count = 0;
foreach ($tags as $i => $htag) {
$count++;
$attributes = array();
$href = $count;
$newId = 'id="' . $href . '"';
$newhtag = '><span style="position: absolute; margin-top: -60px;" ' .$newId. '></span>';
$htagr = str_replace('>' . $titles[$i], "t" . $newhtag . $titles[$i], $htag);
$searches[] = $htag;
$replaces[] = $htagr;
if ((int)$levels[$i] === (int)$_level):
if($num_on){
$chapters[$_level-1] = ((int)$chapters[$_level-1]+1);
$chapter = implode('.', array_slice($chapters, 1, ($levels[$i]-1) ) );
$toc .= '<li><span>' . strval($chapter) . '</span> <a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}else{
$toc .= '<li><a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}
endif;
if ($levels[$i] > $_level) {
$_steps = ((int) $levels[$i] - (int) $_level);
for ($j = 0; $j < $_steps; $j++):
$toc .= '<ol class="continue">';
$chapters[$levels[$i]-1+$j] = (int)$chapters[$levels[$i]-1+$j]+1;
$_level++;
endfor;
$chapter = implode('.', array_slice($chapters, 1, ($levels[$i]-1) ) );
if($num_on){
$toc .= '<li><span>' . strval($chapter) . '</span> <a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}else{
$toc .= '<li><a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}
}
if ($levels[$i] < $_level) {
$_steps = ((int) $_level - (int) $levels[$i]);
$chapters[$levels[$i]-1] = (int)$chapters[$levels[$i]-1]+1;
$_olevel = $_level;
for ($j = 0; $j < $_steps; $j++):
$chapters[$levels[$i]+$j] = 0;
$toc .= '</ol>';
$_level--;
endfor;
$chapters[$_olevel-1] = 0;
$chapter = implode('.', array_slice($chapters, 1, ($levels[$i]-1) ) );
if($num_on){
$toc .= '<li><span>' . strval($chapter) . '</span> <a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}else{
$toc .= '<li><a href="#' . $href . '">' . $titles[$i] . '</a></li>';
}
}
}
$toc .= '</ul>';
$toc .= '</div><div class="clear"></div>';
$content = str_replace($searches, $replaces, $content);
$content = $toc . $content;
endif;
return $content;
}
/**
* Appends the table of content to the $content
* AKA. Executes our filter
*
* @param type $content
* @return type
*/
static function writeToc($content) {
$content = TableOfContents::generateTableOfContents(4, $content, TRUE);
return $content;
}
}
add_filter('the_content', array('TableOfContents', 'writeTOC'));
The easiest way would be to replace
with
This would do a search for the string [toc] and replace it with the table of contents.
It might be more complicated due to how the plugin would work, but on paper it is simple.