Last active
November 21, 2023 16:39
-
-
Save deanebarker/63a67eb52c1fbc3522fc715669e91cb7 to your computer and use it in GitHub Desktop.
Web Component to embed a dynamic table of contents based on heading tags
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
/* | |
<table-of-contents title="Contents" source=".article" selector="h2,h3"></table-of-contents> | |
* "title" will be placed in a HEADER tag as the first child | |
* "source" default to the BODY tag | |
* "selector" defaults to "h2" | |
OR | |
<table-of-contents source=".article" selector="h2,h3"> | |
<header><em>This is the table of contents...</em></header> | |
<!-- items will be added here --> | |
</table-of-contents> | |
It will use the innerText of the selected element, wrapped in a DEV, or this can be over-ridden with an attribute of "data-toc-title". | |
<h2>This will be used as the title</h2> | |
<h2 data-toc-title="This will be used instead">This will NOT be used as the title</h2> | |
*/ | |
class TableOfContents extends HTMLElement { | |
get selector() { | |
return this.getAttribute('selector') ?? 'h2'; | |
} | |
set selector(value) { | |
this.setAttribute('selector', value); | |
} | |
get source() { | |
return this.getAttribute('source') ?? 'body'; | |
} | |
set source(value) { | |
this.setAttribute('source', value); | |
} | |
get title() { | |
return this.getAttribute('title'); | |
} | |
set title(value) { | |
this.setAttribute('title', value); | |
} | |
constructor() { | |
super(); | |
document.addEventListener('DOMContentLoaded', () => this.init()); | |
} | |
static get observedAttributes() { | |
return [ "selector", "title", "source" ]; | |
} | |
attributeChangedCallback() { | |
this.init(); | |
} | |
init() { | |
let items = document.querySelector(this.source).querySelectorAll(this.selector); | |
if(items.length == 0) { | |
this.style.display = "none"; | |
return; | |
} | |
if(this.title != null) { | |
var header = document.createElement("header"); | |
header.innerHTML = this.title; | |
this.appendChild(header); | |
} | |
var counter = 1; | |
items.forEach((e) => { | |
e.setAttribute("id", "h" + counter); | |
counter++; | |
var link = document.createElement("a"); | |
link.setAttribute("href", "#" + e.getAttribute("id")); | |
link.setAttribute("class", "tocItem" + e.tagName); | |
if (e.getAttribute("data-toc-title")) { | |
link.innerHTML = e.getAttribute("data-toc-title"); | |
} else { | |
link.innerHTML = e.innerText; | |
} | |
var item = document.createElement("div"); | |
item.appendChild(link); | |
this.appendChild(item); | |
}); | |
} | |
} | |
customElements.define("table-of-contents", TableOfContents); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment