Paged.js — Build a Table of Contents from your HTML

print this pageBuild a Table of Contents from your HTMLwritten by Julie Blanc on 1 January 2020You got some HTML content you want to make a book with, but it would takes too much time to set the ids and hyperlinks? We got you covered!guidepagedjshooksIn this part of the doc, we'll show you how to build a table of contents using a custom script and the css function target-counter() for the content property.Build the table of contents from your htmlIn HTML, a table of contents is a <nav> elements that contain a list of the reveleant titles of your document with a link to the unique identifier of each. This part can be done with your own tool/generator but here is an exemple of a script to generate a table of contents in vanilla javascript:function createToc(config){ const content = config.content; const tocElement = config.tocElement; const titleElements = config.titleElements; let tocElementDiv = content.querySelector(tocElement); let tocUl = document.createElement("ul"); = "list-toc-generated"; tocElementDiv.appendChild(tocUl); // add class to all title elements let tocElementNbr = 0; for(var i= 0; i < titleElements.length; i++){ let titleHierarchy = i + 1; let titleElement = content.querySelectorAll(titleElements[i]); titleElement.forEach(function(element) { // add classes to the element element.classList.add("title-element"); element.setAttribute("data-title-level", titleHierarchy); // add id if doesn't exist tocElementNbr++; idElement =; if(idElement == ''){ = 'title-element-' + tocElementNbr; } let newIdElement =; }); } // create toc list let tocElements = content.querySelectorAll(".title-element"); for(var i= 0; i < tocElements.length; i++){ let tocElement = tocElements[i]; let tocNewLi = document.createElement("li"); tocNewLi.classList.add("toc-element"); tocNewLi.classList.add("toc-element-level-" + tocElement.dataset.titleLevel); tocNewLi.innerHTML = '<a href="#' + + '">' + tocElement.innerHTML + '</a>'; tocUl.appendChild(tocNewLi); } } Copy this script to a .js and link this file to your document.The table of contents need to be generated before paged.js fragment the content into pages. Therefore, you need to register the handler beforeParsed() and call the table of contents script inside.Add this code in the head of you html document after the call for paged.js script:<script> class handlers extends Paged.Handler { constructor(chunker, polisher, caller) { super(chunker, polisher, caller); } beforeParsed(content){ createToc({ content: content, tocElement: '#my-toc-content', titleElements: [ '.mw-content-ltr h2', 'h3' ] }); } } Paged.registerHandlers(handlers); </script> Configuring the scripttocElement: define the id element where the toc list will be createdtitleElements: array of the title element you want in the your table of contents. You can add as many as you want and the elements can be selected like any css, for example: .title-1 or .my-content h1Generate page numbersThanks to the previous script, your content is now structured using id for the heading, and the table of contents we created use hyperlinks to those #id:<!-- the headings in the text--> <h1 id="pre-digital_era" class="title-element" data-title-level="h1">Pre-digital era</h1> <p>Content...</p> <h1 id="digital_era" class="title-element" data-title-level="h1">Digital era</h1> <p>Content...</p> <!-- the table of contents--> <ul id="toc"> <li><a href="#pre-digital_era">Pre-digital era</a></li> <li><a href="#digital_era">Digital era</a></li> </ul> In the CSS, the target-counter property is used within ::before and ::after pseudo-elements, using the content property. It can be translated as: find the counter named page that appears where you find the element we’re targetting with the attribute href:#toc li a::after{ content: target-counter(attr(href), page); float: right; } Add styles to the table of contentsIf you need to add counters or leaders to your table of contents generated above, here is an exemple of CSS you can use:/* set the style for the list numbering to none */ #list-toc-generated{ list-style: none;} #list-toc-generated .toc-element a::after{ content: " p. " target-counter(attr(href), page); float: right; } #list-toc-generated .toc-element-level-1{ margin-top: 25px; font-weight: bold; } #list-toc-generated .toc-element-level-2{ margin-left: 25px; } /* counters */ #list-toc-generated{ counter-reset: counterTocLevel1; } #list-toc-generated .toc-element-level-1{ counter-increment: counterTocLevel1; counter-reset: counterTocLevel2; } #list-toc-generated .toc-element-level-1::before{ content: counter(counterTocLevel1) ". "; padding-right: 5px; } #list-toc-generated .toc-element-level-2{ counter-increment: counterTocLevel2; } #list-toc-generated .toc-element-level-2::before{ content: counter(counterTocLevel1) ". " counter(counterTocLevel2) ". "; padding-right: 5px; } /* hack for leaders */ #list-toc-generated{ overflow-x: hidden; } /* fake leading */ #list-toc-generated .toc-element::after{ content: ".............................................." ".............................................." ".............................................." "........"; float: left; width: 0; padding-left: 5px; letter-spacing: 2px; } #list-toc-generated .toc-element{ display: flex; } #list-toc-generated .toc-element a::after{ position: absolute; right: 0; background-color: white; padding-left: 6px; } #list-toc-generated .toc-element a{ right: 0; } And voilà! All the files (scripts, CSS, HTML exemples) to create a table of contents are available on gitlab: Media approaches : page floats

This is a companion discussion topic for the original entry at

I just tried to use the code for generating the TOC, but there is some line generated with the wrong page number, I didn’t really change the code.

Please can you tell me if there is something wrong with the code?

I got the solution!

During the generation of my code, I used the same id for the chapter name and the subchapter name, and the interpreter was considering the first chapter id and the first sub chapters ids.

Using a unique id for each chapter and subchapter solved the problem, I generated a correct Table of Contents.

1 Like


HTML validator is a good tool when it comes to this, because it would have told you this.
Happy that it works fine for you!