Automatically generate a content table in WordPress

A content table is what you can use in some big and small documents to reference all the titles so as to give quick access to certain part of the document.

You see that often on FAQs pages for example or other long page where inner navigation is needed.

The point here is to automate the process of creating a content table in WordPress. We are going to produce a list of a certain type of headers at the top of the page/post and link them with anchors to the document. Something similar to this:

Sounds good but there is a limitation. We are not going to support nested titles list for now. It is possible but it would make the code much more complex.

The code is pretty simple and will consist of one function hooked to the filter “the_content” available in WordPress. Using a shortcode like content_table tag=”h2″  between [] will tell the function to generate the table.

Let’s get startedBack to top

Here is the full code. I’ll go in more details below.

/**
 * Function to auto generate a content table from the content
 * @author Ben  http://wordpressthemescollection.com
 * @since 1
 */
add_filter('the_content', 'get_content_table');

function get_content_table($content){

	preg_match('/\[content_table\s(.*?)\]/', $content, $m);
	if($m[0]):
		$attr = shortcode_parse_atts($m[1]);
		
		$in_tag = $attr['tag'];

		preg_match_all('|<'.$in_tag.'>(.*)</'.$in_tag.'>|', $content, $titles);
		
		//if there are titles, build the content table
		if($titles[1]){

			$max = count($titles[1]);
			$counter = 0;
			$content_table = "<div id='content_table'><a name='go_content_table' id='go_content_table'></a><h3>Content table</h3><ul class='xoxo ct_column'>";
			foreach($titles[1] as $title):

				if($counter >= ceil($max/2)){
					$content_table .= "</ul><ul class='xoxo ct_column'>";
					$counter = 0;
				}
				$counter++;

				$content_table .= '<li><a href="#'.slug($title).'">'.$title.'</a></li>';

			endforeach;
			$content_table .= "</ul></div>";
		}
		//echo $content_table;
		//replace the shortcode by the content table
		$content = str_replace('<p>'.$m[0].'</p>', $content_table, $content);

		//add "back to top" links in headers
		$content = preg_replace("|<".$in_tag.">(.*)</".$in_tag.">|e", "'<'.$in_tag.' name=\"'.slug('$1').'\" id=\"'.slug('$1').'\">$1<a class=\"backtop\"href=\"#go_content_table\">Back to top</a></'.$in_tag.'>'", $content);

	endif;

	return $content;
}

function slug($string)
{
	$slug = trim($string);
	$slug= preg_replace('/[^a-zA-Z0-9 -]/','', $slug); // only take alphanumerical characters, but keep the spaces and dashes too...
	$slug= str_replace(' ','-', $slug); // replace spaces by dashes
	$slug= strtolower($slug); // make it lowercase
	return $slug;
}

So, what have we got there.Back to top

add_filter('the_content', 'get_content_table');

The function add_filter is a built function in WordPress which link functions to a particular trigger. In this case we use the filter the_content, which is called just before WordPress prints out the content of a post/page. The filter will pass the content of the post/page to each functions hooked to it. Now all we need is work on the content using the function get_content_table we’ve just hooked.

The get_content_table function.Back to top

preg_match('/[content_tables(.*?)]/', $content, $m);
	if($m[0]):

First we need to find out if the content table is requested so we search for the string content_table (parameters) (between []). If we find one then we carry on, if not we just return the content untouched.

$attr = shortcode_parse_atts($m[1]);

$in_tag = $attr['tag'];

preg_match_all('|<'.$in_tag.'>(.*)</'.$in_tag.'>|', $content, $titles);

If the shortcode is there we need to get which tag needs to be used to build the table. WordPress provides a function to do that in the shortcode api. Once we have the targeted tag we can do a search for it in the content and put the results in an array.

Now the interesting stuff:Back to top

if($titles[1]){

	$max = count($titles[1]);
	$counter = 0;
	$content_table = "<div id='content_table'><a name='go_content_table' id='go_content_table'></a><ul class='xoxo ct_column'>";
	foreach($titles[1] as $title):

		if($counter >= ceil($max/2)){
			$content_table .= "</ul><ul class='xoxo ct_column'>";
			$counter = 0;
		}
		$counter++;

		$content_table .= '<li><a href="#'.slug($title).'">'.$title.'</a></li>';

	endforeach;
	$content_table .= "</ul></div>";
}

If the right type of titles are present then we start building the  content_table. First we find out how many elements we’ve got then create a div container to hold everything and start the first unordered list.

When we reach half of the elements we close the first ul and open a new one to create to list. This will help if you want to split the content table in two columns. Note: we have to slug the titles to use them as anchor links and keep the code valid.

The next is to add the back to top link to the existing titles:

$content = preg_replace("|<".$in_tag.">(.*)</".$in_tag.">|e", "'<'.$in_tag.' name="'.slug('$1').'" id="'.slug('$1').'">$1<a class="backtop"href="#go_content_table">Back to top</a></'.$in_tag.'>'", $content);

We use the power of regular expressions and callback function to get that sorted. I think this was the trickiest bit of the code.

If you place this code in you function folder and add a the content_table tag=”X” (between [], don’t forget the double quotes around the tag) shortcode (where X is the tag used for your titles) then you should get a nice content table where you have added the shortcode. Add some simple styling and you’re done!

15 Comments

  1. Posted October 12, 2009 at 1:37 pm | Permalink

    Cool stuff but i get an error:

    Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING in /PATH-TO-FILE/functions.php on line 190

    In line 190 is the part for the “back to top” links:

    $content = preg_replace("|(.*)|e", "''.ucfirst('$1').'Back to top'", $content);

    Any idea what´s happened?

  2. admin
    Posted October 12, 2009 at 3:46 pm | Permalink

    Works for me when I tried you line of code for the ‘back to top’ bit. Could be a problem with php. Some early versions (before 4.0.4) do not allow you to use the $n in the second parameter of preg_replace.

  3. Posted October 12, 2009 at 4:49 pm | Permalink

    Needed to set some backslashes in front of some doublequotes and now the error is gone. I´m Running PHP 5.

    But it still does not wor properly. When I type in

    [content_table tag=h3]

    in order to get the h3 headings in a list the output is just the shortcode as plain text. D I make any mistake?

  4. admin
    Posted October 13, 2009 at 2:14 pm | Permalink

    That’s my bad, you have to enclose the tag in double quotes. So the shortcode should look like: [content_table tag="h3"] in your case.

    I’ve corrected that in the article as well.

  5. Posted October 13, 2009 at 5:40 pm | Permalink

    Unfortunately still the same. Now it appears as plain text WITH double quotes. Also tried single quotes…doesn´t work :-(

  6. admin
    Posted October 13, 2009 at 6:56 pm | Permalink

    Ok let’s try something else. Is you shortcode between p tags? The function only replaces the string <p>[content_table tag="xx"]</p>. (check spaces as well)

    If still no luck try to echo at various stage of the function to see where it fails.

  7. Posted October 13, 2009 at 7:05 pm | Permalink

    A look into the source shows:

    [content_table tag="h3"]

    what do you mean “echo at various stages…”?

  8. admin
    Posted October 13, 2009 at 8:26 pm | Permalink

    If there is no p tags around the shortcode then try to change the line $content = str_replace('<p>'.$m[0].'</p>', $content_table, $content);

    by

    $content = str_replace($m[0], $content_table, $content);

    By echo I meant using the echo function in php to print out some variable like $content_table before doing the replace. Just to double check that the function acually works.

  9. Posted October 14, 2009 at 7:04 am | Permalink

    Sorry but it still doesn´t work. My shortcode IS between tags but they where stripped out in my last comment.

    The “echo” doesn´t seem to work but I´m not so much a PHP guy and not sure where exactly to put it.

    Maybe something went wrong when I copy&paste the code for the functions file. Is it possible you send it to me via email in a txt file?

  10. Posted October 14, 2009 at 7:05 am | Permalink

    p tags where stripped out again…

  11. Posted October 14, 2009 at 9:46 am | Permalink

    After I used the code you sent me via email it works perfectly. Thanks so much for that.

  12. Amanda
    Posted March 3, 2010 at 7:20 pm | Permalink

    Is there a way to do this outside of the loop? For example, to put the contents table in the sidebar?

  13. Amanda
    Posted March 4, 2010 at 12:13 am | Permalink

    Nevermind, I was able to piece something together by modifying this a bit and using the shortcode in a text widget.

    What I ended up with is a bit specific to my site, and I’m sure there’s a better way to go about it, but here it is if someone wants to play with it:
    http://pastie.org/852611

    I’m going to always use h3’s, so I removed the need for the “tag” attribute (so the shortcode is just [content_table]), and since it’s in the sidebar, I’ll never need to divide it in half, so I removed that bit too, along with some slight markup changes, etc.

  14. Amanda
    Posted March 4, 2010 at 12:14 am | Permalink

    Almost forgot: Thank you very much for this, by the way!

  15. admin
    Posted March 5, 2010 at 12:05 pm | Permalink

    Thanks for your comment Amanda and great job on getting it working in the sidebar! That’s something I was thinking about but never got the time to get around it, thanks a lot.

3 Trackbacks

  1. By Automaticly generate a content table in WordPress on October 8, 2009 at 7:00 pm

    [...] Visit Source. [...]

  2. [...] the original post: Automatically generate a content table in WordPress | Wordpress Themes Collection Comments0 Leave a Reply Click here to cancel [...]

  3. [...] Automatically Generate A Content Table In WordPress [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comment moderation is enabled. Your comment may take some time to appear.