From 5b079b87507de9a010b6b493f82c753a5d263df7 Mon Sep 17 00:00:00 2001 From: tompng Date: Tue, 18 Nov 2025 03:03:01 +0900 Subject: [PATCH 1/2] Stable calculation of active toc IntersectionObserver only notifies changed intersections. We need to track which heading tag currently intersects with viewport. Use the top-most intersecting heading to make user-clicked toc links match with active toc links. --- lib/rdoc/generator/template/aliki/js/aliki.js | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index 599dc4a15d..fc4e85db93 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -209,54 +209,66 @@ function generateToc() { function hookTocActiveHighlighting() { var tocLinks = document.querySelectorAll('.toc-link'); - if (tocLinks.length === 0) return; + var targetHeadings = []; + tocLinks.forEach(function(link) { + var targetId = link.getAttribute('data-target'); + var heading = document.getElementById(targetId); + if (heading) { + targetHeadings.push(heading); + } + }); + + if (targetHeadings.length === 0) return; var observerOptions = { root: null, - rootMargin: '-20% 0px -35% 0px', + rootMargin: '0% 0px -35% 0px', threshold: 0 }; - var activeLink = null; + var intersectingHeadings = new Set(); + function update() { + var firstIntersectingHeading = targetHeadings.find(function(heading) { + return intersectingHeadings.has(heading); + }); + if (!firstIntersectingHeading) return; + var correspondingLink = document.querySelector('.toc-link[data-target="' + firstIntersectingHeading.id + '"]'); + if (!correspondingLink) return; + + // Remove active class from all links + tocLinks.forEach(function(link) { + link.classList.remove('active'); + }); + + // Add active class to current link + correspondingLink.classList.add('active'); + activeLink = correspondingLink; + + // Scroll link into view if needed + var tocNav = document.querySelector('#toc-nav'); + if (tocNav) { + var linkRect = correspondingLink.getBoundingClientRect(); + var navRect = tocNav.getBoundingClientRect(); + if (linkRect.top < navRect.top || linkRect.bottom > navRect.bottom) { + correspondingLink.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + } + } var observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { - var id = entry.target.id; - var correspondingLink = document.querySelector('.toc-link[data-target="' + id + '"]'); - - if (correspondingLink) { - // Remove active class from all links - tocLinks.forEach(function(link) { - link.classList.remove('active'); - }); - - // Add active class to current link - correspondingLink.classList.add('active'); - activeLink = correspondingLink; - - // Scroll link into view if needed - var tocNav = document.querySelector('#toc-nav'); - if (tocNav) { - var linkRect = correspondingLink.getBoundingClientRect(); - var navRect = tocNav.getBoundingClientRect(); - - if (linkRect.top < navRect.top || linkRect.bottom > navRect.bottom) { - correspondingLink.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - } - } - } + intersectingHeadings.add(entry.target); + } else { + intersectingHeadings.delete(entry.target); } }); + update(); }, observerOptions); // Observe all headings that have corresponding TOC links - tocLinks.forEach(function(link) { - var targetId = link.getAttribute('data-target'); - var targetHeading = document.getElementById(targetId); - if (targetHeading) { - observer.observe(targetHeading); - } + targetHeadings.forEach(function(heading) { + observer.observe(heading); }); // Smooth scroll when clicking TOC links From 2d2f569f929fb86f282e8decdf0b5534f67c2f0c Mon Sep 17 00:00:00 2001 From: tomoya ishida Date: Tue, 18 Nov 2025 20:22:04 +0900 Subject: [PATCH 2/2] Apply suggestion from @kou Remove assignment to unused variable Co-authored-by: Sutou Kouhei --- lib/rdoc/generator/template/aliki/js/aliki.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/rdoc/generator/template/aliki/js/aliki.js b/lib/rdoc/generator/template/aliki/js/aliki.js index fc4e85db93..5fcc3ac317 100644 --- a/lib/rdoc/generator/template/aliki/js/aliki.js +++ b/lib/rdoc/generator/template/aliki/js/aliki.js @@ -242,7 +242,6 @@ function hookTocActiveHighlighting() { // Add active class to current link correspondingLink.classList.add('active'); - activeLink = correspondingLink; // Scroll link into view if needed var tocNav = document.querySelector('#toc-nav');