Creating a Dynamic Table of Contents for PHPKB
Wednesday, March 25, 2015 at 3:13PM
Kevin in Code, JavaScript, JavaScript, PHP

We use phpkb at work for an internal knowledge base and overall I'm pretty happy with it, it does pretty much everything that I wanted in a KB except for one or two things. One of those was to allow for a dynamic table of contents based on use of heading tags in your article, something that most wiki software does out of the box, as does MS Word, which is where much of the content we are migrating into phpkb lived before.

So I wrote a little bit of JavaScript to enable a dymanic TOC myself. It is easy to implement if you are using phpkb, here is how it looks and how to turn this on for article pages.

The TOC is inserted at the top of your article and indented based on heading use

You can cheat and just get the jscript file and my modded article.php in this zipped copy.

Step 1 - Modify article.php (back it up first..)
Paste these lines in that page right before the '$include_files' insertion.

<script type=\"text/javascript\" src=\"$path_kb/thesaurus/js/jquery1-7-1.js\"></script>
<script type=\"text/javascript\" src=\"$path_kb/include/toc.js\"></script>


Step 2 - Create a custom field called 'Show TOC' and make it a non-required checkbox with Yes,No options.


Step 3 - Drop the toc.js file into the include folder for your installation. Here is the source for that file so you can see what is going on.

$(document).ready(function(){
	$('.customfields').find('li').each(function(i) {  // Looks at custom fields on page to see if enabled
		var current = $(this);
		if(current.text() == 'Show TOC: Yes') {  // Enabled, show table of contents		
			$("#ARTICLECONTENT").prepend('<div id="toc"><b>Table of Contents</b></div>');
			$("#toc").css('border','1px solid #eccf59');
			$("#toc").css('background','#f1eddf');
			$("#toc").css('margin','5px 0 0 0');
			$("#toc").css('padding','5px 5px 15px 5px');
			$("#toc").append("<ul>");
			var iToc = 0; // to track if we find any headings
			$('article').children("h1, h2, h3, h4, h5").each(function(i) {
				iToc ++;
				var current = $(this);
				current.attr("id", "title" + i);
				var sp = '';
				switch(current.prop("tagName")){
					case 'H1': sp = '10px'; break;
					case 'H2': sp = '20px'; break;
					case 'H3': sp = '30px'; break;
					case 'H4': sp = '40px'; break;
					case 'H5': sp = '50px'; break;				
				}
				if(current.html().trim()){
					$("#toc").append("<li style='padding-left:" + sp + ";'><a id='link" + i + 
						"' href='#title" + i + "' title='" + current.attr("tagName") + 
						"'>" + current.html() + "</a></li>");
				}
			});	
			$("#toc").append("</ul>");
			if(iToc == 0) { $("#toc").hide(); } // if no content for TOC we just hide it			
		}
	});
});

As you can see, with some very little changes you can repurpose this for other applications that use the same 'H tag' heading hierarchy that you commonly run into.

Article originally appeared on KevinGuyer.com - Stranded on Midgard (http://kevinguyer.squarespace.com/).
See website for complete article licensing information.