Created
March 16, 2022 16:49
-
-
Save paceaux/12ce437e0f7ab1d5a9326d92e68710c0 to your computer and use it in GitHub Desktop.
A class for generating a table of contents on a page.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TableOfContents { | |
static listContainerClass = 'articleTOC__list'; | |
static listItemClass = 'articleTOC__item'; | |
static listLinkClass = 'articleTOC__link'; | |
static modifierSeparator = '--'; | |
/* | |
* @description Gets a list of all title elements having an ID in a given container | |
* @param {HTMLElement} container - An element containing title elements (h1, h2, h3) | |
* | |
* @returns {Nodelist} | |
*/ | |
static getTitleElements(container) { | |
return container.querySelectorAll('h2[id], h3[id], h4[id], h5[id], h6[id]'); | |
} | |
/* | |
* @description Returns a <ul> | |
* | |
* @returns {HTMLElement} a UL element with the classname articleTOC__list | |
*/ | |
static getListElement(listElClass = TableOfContents.listContainerClass) { | |
const listEl = document.createElement('ul'); | |
listEl.classList.add(listElClass); | |
return listEl; | |
} | |
/* | |
* @description determines the class name that should be applied to a node | |
* @param {HTMLElement} titleNode - the html element that is an h2-6 | |
* @param {string} [baseName] - the base classname | |
* @param {string} [modifierSeparator] - the thing that separates the base name and the modified part of the name | |
* | |
* @returns {string} the full class to be used on the node as `{baseName}{modifierSeparator}{modifierName} | |
*/ | |
static getItemModifierClass(titleNode, baseName = TableOfContents.listItemClass, modifierSeparator = TableOfContents.modifierSeparator) { | |
if (!titleNode) return; | |
const { tagName} = titleNode; | |
let modifierName = 'biggest'; | |
switch (tagName) { | |
case 'H3': | |
modifierName = 'bigger'; | |
break; | |
case 'H4': | |
modifierName = 'big'; | |
break; | |
case 'H5': | |
modifierName = 'base'; | |
break; | |
case 'H6': | |
modifierName = 'small'; | |
default: | |
break; | |
} | |
return `${baseName}${modifierSeparator}${modifierName}`; | |
} | |
/* | |
* @description generates an <li> with an appropriate class name and a nested <a> | |
* @param {HTMLElement} titleNode The H1-5 element with an id that needs a link to it | |
* @param {string} [itemElClass = TableofContents.listItemClass] | |
* @param {string} [linkElClass] | |
* @param {modifierSeparator} | |
* | |
* @returns {HTMLElement} <li><a>; | |
*/ | |
static getItemElement( | |
titleNode, | |
itemElClass=TableOfContents.listItemClass, | |
linkElClass=TableOfContents.listLinkClass, | |
modifierSeparator = TableOfContents.modifierSeparator | |
) { | |
const listEl = document.createElement('li'); | |
const linkEl = document.createElement('a'); | |
const href = `#${titleNode.id}`; | |
const title = titleNode.innerText; | |
listEl.classList.add(itemElClass); | |
listEl.classList.add(TableOfContents.getItemModifierClass(titleNode, itemElClass, modifierSeparator)) | |
linkEl.classList.add(linkElClass); | |
linkEl.setAttribute('href', href); | |
linkEl.innerText = title; | |
listEl.appendChild(linkEl); | |
return listEl; | |
} | |
tableOfContents = null; | |
/* | |
* @param {HTMLElement} [articleContainer] an HTML element with h1-5 that have ids on them | |
* @param {HTMLElement} [tocContainer] | |
* @param {object} [configuration] | |
* | |
* @example | |
* const tocArticle = document.querySelector('.entry.shouldHaveTOC'); | |
* const tocWrapper = document.querySelector('.articleTOC'); | |
* | |
* const tableofContents = new TableOfContents(tocArticle, tocWrapper, {listContainerClass: 'foo', listItemClass: 'bar', listLinkClass: 'baz'}); | |
*/ | |
constructor( | |
articleContainer, | |
tocContainer, | |
{ | |
listContainerClass = TableOfContents.listContainerClass, | |
listItemClass = TableOfContents.listItemClass, | |
listLinkClass = TableOfContents.listLinkClass, | |
modifierSeparator = TableOfContents.modifierSeparator, | |
} = {} | |
) { | |
this.listContainerClass = listContainerClass; | |
this.listItemClass = listItemClass; | |
this.listLinkClass = listLinkClass; | |
this.modifierSeparator = modifierSeparator; | |
this.articleContainer = articleContainer; | |
this.tocContainer = tocContainer; | |
this.generateTOC(this.articleContainer, this.tocContainer); | |
} | |
/* | |
* @description generates a <ul> element with <li><a> nested inside | |
* @param {NodeList} titleNodes The H1-5 elements with ids that need | |
* | |
* @returns {HTMLElement} <ul><li><a>; | |
*/ | |
getTOCElement(titleNodes) { | |
const listEl = TableOfContents.getListElement(this.listContainerClass, this.listContainerClass); | |
[...titleNodes].forEach((titleNode) => { | |
const itemEl = TableOfContents.getItemElement(titleNode, this.listItemClass, this.listLinkClass, this.modifierSeparator); | |
listEl.appendChild(itemEl); | |
}); | |
return listEl; | |
} | |
/* | |
* @description Generates a table of contents | |
* @param {HTMLElement} [articleContainer] an HTML element with h1-5 that have ids on them | |
* @param {HTMLElement} [tocContainer] | |
*/ | |
generateTOC(articleContainer = this.articleContainer, tocContainer = this.tocContainer) { | |
if (!articleContainer || !tocContainer) return; | |
try { | |
const titles = TableOfContents.getTitleElements(articleContainer); | |
const tocEl = this.getTOCElement(titles); | |
console.log(tocEl); | |
this.tableOfContents = tocEl; | |
tocContainer.appendChild(tocEl); | |
} catch (generateTOCError) { | |
console.error(generateTOCError); | |
} | |
} | |
} | |
const tocArticle = document.querySelector('.entry.shouldHaveTOC'); | |
const tocWrapper = document.querySelector('.articleTOC'); | |
const tableofContents = new TableOfContents(tocArticle, tocWrapper, {listContainerClass: 'foo', listItemClass: 'bar', listLinkClass: 'baz'}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment