@firstandthird/toc
Version:
Table Of Content Library
109 lines (87 loc) • 2.74 kB
JavaScript
import { find, findOne, ready, on, fire } from 'domassist';
import scrollTriggers from 'scroll-triggers';
import { init as smoothScroll, scroll } from 'smooth-scroller';
function init(el) {
if (!el) {
el = find('[data-toc]');
el.forEach(e => init(e));
return;
}
if (!el) {
return;
}
const container = el.dataset.tocContainer ?
findOne(el.dataset.tocContainer) || document.body : document.body;
const selectors = el.dataset.toc.split(',').map(s => s.trim());
const tocItems = [];
const offset = el.dataset.tocOffset ? parseInt(el.dataset.tocOffset, 10) : 1;
let i = 1;
// Building dict
selectors.forEach(selector => {
const items = find(selector, container);
items.forEach(item => {
// Keep the id if already there
const index = item.id || `toc-${i++}`;
const text = item.dataset.tocTitle ?
item.dataset.tocTitle.trim() : item.textContent.trim();
const sanitizedClassName = selector.replace(/((:+[\w-\d]*)|[^A-z0-9-\s])/g, ' ').replace(/\s{2,}/g, ' ').trim();
const className = `toc-${sanitizedClassName}`;
// Set it if none
if (item.id !== index) {
item.id = index;
}
tocItems.push({ index, text, className });
});
});
let html = '<ul>';
const triggerOptions = [];
// Building markup
tocItems.forEach((item, j) => {
const nextEl = tocItems[j + 1];
const options = {
el: `.toc-li-${j}`,
fixed: 'true',
start: `#${item.index}`,
position: 'top',
positionEnd: 'top',
className: 'toc-visible'
};
html += `\n<li class="toc-li-${j} ${item.className}"><a href="#${item.index}">${item.text}</a></li>`;
if (nextEl) {
options.end = `#${nextEl.index}`;
}
triggerOptions.push(options);
});
html += '</ul>';
el.innerHTML = html;
const tocs = find('li', el);
const anchors = find('a', el);
// Setting up scroll triggers and smooth scroll
scrollTriggers(triggerOptions);
smoothScroll(anchors, offset);
// Pause scroll triggers while smoothscrolling
on(document.body, 'smoothscroll:start', () => {
fire(tocs, 'scrolltriggers:pause');
});
on(document.body, 'smoothscroll:end', () => {
fire(tocs, 'scrolltriggers:resume');
fire(window, 'scroll');
});
if (window.location.hash) {
anchors.some(anchor => {
const found = anchor.getAttribute('href') === window.location.hash;
if (found) {
setTimeout(() => {
const element = findOne(window.location.hash);
if (element) {
// Silent scroll to element
scroll(element, null, offset, true);
}
});
}
return found;
});
}
}
export default init;
ready(init);