I have headers in through
tags. Is there a way that I can use JavaScript to generate a table of contents for the contents tha
Check out the component you are looking for on this page: Re-inventing XMLHttpRequest: Cross-browser implementation with sniffing capabilities
It walks over entire document and creates a TOC with all h1-h6 elements reflected in an openable (upon hover) structure. The component is standalone and it doesn't use any library.
JQuery comes to mind as a fast and easy solution. A quick google search for jquery table of contents yields two promising results:
let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
let list = document.createElement('ol');
let _el = list;
for(i=0; i<headers.length; i++) {
while(_el) {
let li = document.createElement('li');
li.innerText = headers[i].innerText;
li.setAttribute('tagName', headers[i].tagName);
if(_el.getAttribute('tagName') < headers[i].tagName) {
let ol = _el.children.length > 0 ? ol = _el.querySelector('ol') : document.createElement('ol');
ol.appendChild(li);
_el.appendChild(ol);
_el = li;
break;
} else {
if(_el.tagName == 'OL') {
_el.appendChild(li);
_el = li;
break;
} else if (!_el.parentNode.parentNode) {
_el.parentNode.appendChild(li);
_el = li;
break;
}
else {
_el = _el.parentNode.parentNode;
}
}
}
}
console.log(list);
Are you looking for a prepackaged solution or are you asking how this can be implemented?
For the latter, you could use XPath to iterate through all getElementsByTagName()
recursively on <h1>
through <h6>
<h*>
elements and construct the corresponding nested <ul>
or <ol>
lists. You'd also have to add the <a>
tags to the headers.
this.insert = (el, h) => {
let li = document.createElement('li');
li.innerText = h.innerText;
li.setAttribute('tagName', h.tagName);
if(el.tagName == 'OL') {
el.appendChild(li);
return li;
} else if(el.getAttribute('tagName') < h.tagName) {
let ol = el.children.length > 0 ? ol = el.querySelector('ol') : document.createElement('ol');
ol.appendChild(li);
el.appendChild(ol);
return li;
} else if(!el.parentNode.parentNode) {
el.parentNode.appendChild(li);
return li;
} else {
return this.insert(el.parentNode.parentNode, h);
}
}
this.parse = (headers) => {
let list = document.createElement('ol');
let el = list;
for(i=0; i<headers.length; i++) {
el = this.insert(el, headers[i]);
}
return list;
}
let headers = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
let toc = this.parse(headers);
console.log(toc);
Here's a great script to do this:
https://github.com/matthewkastor/html-table-of-contents/wiki
To use it:
Add this tag:
<script src="./node_modules/html-table-of-contents/src/html-table-of-contents.js" type="text/javascript">
Call the function, such as in your body's onload attribute:
<body onload="htmlTableOfContents();">
Here is the definition of the method that does the generation:
/**
* Generates a table of contents for your document based on the headings
* present. Anchors are injected into the document and the
* entries in the table of contents are linked to them. The table of
* contents will be generated inside of the first element with the id `toc`.
* @param {HTMLDOMDocument} documentRef Optional A reference to the document
* object. Defaults to `document`.
* @author Matthew Christopher Kastor-Inare III
* @version 20130726
* @example
* // call this after the page has loaded
* htmlTableOfContents();
*/
function htmlTableOfContents (documentRef) {
var documentRef = documentRef || document;
var toc = documentRef.getElementById('toc');
var headings = [].slice.call(documentRef.body.querySelectorAll('h1, h2, h3, h4, h5, h6'));
headings.forEach(function (heading, index) {
var anchor = documentRef.createElement('a');
anchor.setAttribute('name', 'toc' + index);
anchor.setAttribute('id', 'toc' + index);
var link = documentRef.createElement('a');
link.setAttribute('href', '#toc' + index);
link.textContent = heading.textContent;
var div = documentRef.createElement('div');
div.setAttribute('class', heading.tagName.toLowerCase());
div.appendChild(link);
toc.appendChild(div);
heading.parentNode.insertBefore(anchor, heading);
});
}
try {
module.exports = htmlTableOfContents;
} catch (e) {
// module.exports is not defined
}