WordPress

Add Table of Content (TOC)

This code will generate the table of content from the heading tag used on the single page or post. Refer to the comments on the code to customize the code.

Add the below code to the functions.php file

// declare a function and pass the $content as an argument
function insert_table_of_contents($content) {

  // used to determine the location of the
  // table of contents when $fixed_location is set to false
  $html_comment = "<!--insert-toc-->";
  // checks if $html_comment exists in $content
  $comment_found = strpos($content, $html_comment) ? true : false;
  // set to true to insert the table of contents in a fixed location
  // set to false to replace $html_comment with $table_of_contents
  $fixed_location = true;

  // return the $content if
  // $comment_found and $fixed_location are false
  if (!$fixed_location && !$comment_found) {
    return $content;
  }

  // exclude the table of contents from all pages
  // other exclusion options include:
  // in_category($id)
  // has_term($term_name)
  // is_single($array)
  // is_author($id)
    if (is_page()) {
        return $content;
    }
    
  // regex to match all HTML heading elements 2-6
  $regex = "~(<h([2-6]))(.*?>(.*)<\/h[2-6]>)~";

  // preg_match_all() searches the $content using $regex patterns and
  // returns the results to $heading_results[]
  //
  // $heading_results[0][] contains all matches in full
  // $heading_results[1][] contains '<h2-6'
  // $heading_results[2][] contains '2-6'
  // $heading_results[3][] contains '>heading title</h2-6>
  // $heading_results[4][] contains the title text
  preg_match_all($regex, $content, $heading_results);

  // return $content if less than 3 heading exist in the $content
  $num_match = count($heading_results[0]);
  if($num_match < 3) {
    return $content;
  }

  // declare local variable
  $link_list = "";
  // loop through $heading_results
  for ($i = 0; $i < $num_match; ++ $i) {

      // rebuild heading elements to have anchors
      $new_heading = $heading_results[1][$i] . " id='$i' " . $heading_results[3][$i];

      // find original heading elements that don't have anchors
      $old_heading = $heading_results[0][$i];

      // search the $content for $old_heading and replace with $new_heading
    $content = str_replace($old_heading, $new_heading, $content);

      // generate links for each heading element
      // each link points to an anchor
      $link_list .= "<li class='heading-level-" . $heading_results[2][$i] .
        "'><a href='#$i'>" . $heading_results[4][$i] . "</a></li>";
  }
      
  // opening nav tag
  $start_nav = "<nav class='table-of-content' id='ez-toc-container'>";
  // closing nav tag
  $end_nav = "</nav>";
  // title
  $title = "<h2>Outlines of Contents</h2>";

  // wrap links in '<ul>' element
  $link_list = "<ul>" . $link_list . "</ul>";

  // piece together the table of contents
  $table_of_contents = $start_nav . $title . $link_list . $end_nav;

  // if $fixed_location is true and
  // $comment_found is false
  // insert the table of contents at a fixed location
  if($fixed_location && !$comment_found) {
    // location of first paragraph
    $first_paragraph = strpos($content, '</p>', 0) + 4;
    // location of second paragraph
    $second_paragraph = strpos($content, '</p>', $first_p_pos);
    // insert $table_of_contents after $second_paragraph
  return substr_replace($content, $table_of_contents, $second_paragraph + 4 , 0);
  }
  // if $fixed_location is false and
  // $comment_found is true
  else {
    // replace $html_comment with the $table_of_contents
    return str_replace($html_comment, $table_of_contents, $content);
  }
} 
// pass the function to the content add_filter hook
add_filter('the_content', 'insert_table_of_contents');