Table of contents generator

Installation

jsDeliver CDN

<script src="https://cdn.jsdelivr.net/npm/@thelevicole/toc.js@1/dist/toc.jquery.js"></script>

NPM / Webpack Coming soon!

npm i @thelevicole/toc.js
require('@thelevicole/toc.js');

Or

import '@thelevicole/toc.js'

Examples

By default the table of contents uses <h1>, <h2>, <h3>, <h4>, <h5> and <h6> tags found in the <body> to generate lists and their hierarchy.

So for example, if we had a html page something like...

HTML:

<h1>My page title</h1>

<div class="table-of-contents"></div>

<article>
	<h1>Heading 1</h1>
	<h2>Heading 2</h2>
	<h3>Heading 3</h3>
	<h2>Heading 2</h2>
	<h2>Heading 2</h2>
	<h1>Heading 1</h1>
</article>

And we then initiated the table of contents plugin like so...

Javascript:

(function($) {
	'use strict';

	$('.table-of-contents').tableOfContents();

})(jQuery);

We would end up with something like...

Result:

My page title

Heading 1

Heading 2

Heading 3

Heading 2

Heading 2

Heading 1

More often than not you will probably want to define a content area for the table of contents to generate from instead of the entire <body>. To achieve this we can use the contentTarget option.

For example...

HTML:

<h1>My page title</h1>

<div class="table-of-contents"></div>

<article id="my-content-area">
	<h1>Heading 1</h1>
	<h2>Heading 2</h2>
	<h3>Heading 3</h3>
	<h2>Heading 2</h2>
	<h2>Heading 2</h2>
	<h1>Heading 1</h1>
</article>

And we then initiated the table of contents plugin pasing the DOM selector of our content area. Note that the contentTarget option accepts a jQuery object or a selector string.

Javascript:

(function($) {
	'use strict';

	$('.table-of-contents').tableOfContents({
		contentTarget: '#my-content-area' // Or $('#my-content-area')
	});

})(jQuery);

Now we end up with the following, which excludes the page title as it is not in our content area...

Result:

My page title

Heading 1

Heading 2

Heading 3

Heading 2

Heading 2

Heading 1

If you have large lists with many nesting levels you might want to limit the maximum nesting depth. This can be achieved using the nestingDepth option.

For example...

HTML:

<div class="table-of-contents"></div>

<article>
	<h1>Heading 1</h1>
	<h2>Heading 2</h2>
	<h3>Heading 3</h3>
	<h4>Heading 4</h4>
	<h5>Heading 5</h5>
	<h6>Heading 6</h6>
</article>

And we then initiated the table of contents plugin passing the maximum depth.

Javascript:

(function($) {
	'use strict';

	$('.table-of-contents').tableOfContents({
		nestingDepth: 3
	});

})(jQuery);

Now we end up with the following, which stops nesting at the third depth...

Result:

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Custom selectors can be used for content that doesn't necessarily use heading tags or if you want the tags to act as different nesting depths.

Selector option accepts a string, array or object of selectors and depths:

  • String:
    $('.table-of-contents').tableOfContents({
    	// '{selector}${depth}; {selector}${depth}; ...'
    	selectors: 'h1$1; h2$2; h3$3; p:not(.my-class)$2; ...'
    });

    The selector pattern is fairly straight forward, and follows a simple pattern. We first have the DOM/CSS selector {selector} followed by the nesting depth which starts with a dollar symbol ${depth} and finished with a semicolon ;

    The default pattern used for nested headings looks like this: 'h1$1; h2$2; h3$3; h4$4; h5$5; h6$6;'

  • Array:
    $('.table-of-contents').tableOfContents({
    	selectors: [
    		// '{selector}${depth}'
    		'h1$1',
    		'h2$2',
    		'h3$3',
    		'p:not(.my-class)$2',
    		...
    	]
    });
  • Object:
    $('.table-of-contents').tableOfContents({
    	selectors: {
    		// '{selector}': {depth}
    		'h1': 1,
    		'h2': 2,
    		'h3': 3,
    		'p:not(.my-class)': 2,
    		...
    	}
    });

For example...

HTML:

<div class="table-of-contents"></div>

<article>
	<p class="level-1">I'm level 1</p>
	<p class="level-2">I'm level 2</p>
	<p class="level-1">I'm level 1 again</p>
	<p class="level-2">I'm level 2 again</p>
	<p class="level-3">I'm level 3</p>
	<p><strong>I'm a div element</strong></p>
	<p class="level-2">I'm level 2</p>
</article>

And we then initiated the table of contents plugin passing the selectors and their desired depths.

Javascript:

(function($) {
	'use strict';

	$('.table-of-contents').tableOfContents({
		selectors: '.level-1 $1; .level-2 $2; .level-3 $3; p > strong $4'
	});

})(jQuery);

Now we end up with the following, which stops nesting at the third depth...

Result:

I'm level 1

I'm level 2

I'm level 1 again

I'm level 2 again

I'm level 3

I'm a div element

I'm level 2