toc-highlighter.ts 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. /**
  2. * Highlights the current section in the table of contents when scrolling.
  3. */
  4. export class TocHighlighter {
  5. constructor(private tocElement: Element) {}
  6. highlight() {
  7. const article = document.querySelector('article');
  8. if (this.tocElement && article) {
  9. const headers: HTMLHeadingElement[] = Array.from(article.querySelectorAll('h1[id],h2[id],h3[id]'));
  10. window.addEventListener('scroll', (e) => {
  11. this.highlightCurrentSection(headers);
  12. });
  13. this.highlightCurrentSection(headers);
  14. }
  15. }
  16. private highlightCurrentSection(headers: HTMLElement[]) {
  17. const locationHash = location.hash;
  18. Array.from(this.tocElement.querySelectorAll('.active')).forEach(el => el.classList.remove('active'));
  19. // tslint:disable:prefer-for-of
  20. for (let i = 0; i < headers.length; i++) {
  21. const header = headers[i];
  22. const nextHeader = headers[i + 1];
  23. const id = header.getAttribute('id') as string;
  24. if (!nextHeader || (nextHeader && (window.scrollY + window.innerHeight - 200) < nextHeader.offsetTop)) {
  25. this.highlightItem(id);
  26. return;
  27. }
  28. const isCurrentTarget = `#${id}` === locationHash;
  29. const currentTargetOffset = isCurrentTarget ? 90 : 0;
  30. if ((header.offsetTop + currentTargetOffset) >= window.scrollY) {
  31. this.highlightItem(id);
  32. return;
  33. }
  34. }
  35. }
  36. private highlightItem(id: string) {
  37. const tocItem = this.tocElement.querySelector(`[href="#${id}"]`);
  38. if (tocItem) {
  39. tocItem.classList.add('active');
  40. }
  41. }
  42. }