Added TOC highlighting
This commit is contained in:
parent
f8efb09284
commit
2e597f5b8f
4 changed files with 200 additions and 4 deletions
|
@ -17,9 +17,12 @@
|
|||
}
|
||||
|
||||
.toc:has(#TableOfContents) {
|
||||
background-color: rgba(25, 25, 35, 0.45);
|
||||
background-color: rgba(25, 25, 35, 0.3);
|
||||
border-radius: 10px;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.background-container {
|
||||
|
@ -107,4 +110,45 @@ header button[id^="search-button"]:hover {
|
|||
100% {
|
||||
backdrop-filter: blur(8px) hue-rotate(-10deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* TOC link styling */
|
||||
#TableOfContents a {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
display: block;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
#TableOfContents a:hover {
|
||||
color: white;
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* Active TOC item */
|
||||
#TableOfContents a.active {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transform: translateX(4px);
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
#TableOfContents a.active::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 1rem;
|
||||
background: linear-gradient(to bottom, #4a9eff, #2d7bda);
|
||||
border-radius: 2px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* Remove the default smooth scrolling behavior */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
151
assets/js/toc-highlight.js
Normal file
151
assets/js/toc-highlight.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tocLinks = document.querySelectorAll('#TableOfContents a');
|
||||
const tocContainer = document.querySelector('.toc');
|
||||
const sections = new Map(); // Store section elements with their corresponding TOC links
|
||||
let lastActiveSection = null;
|
||||
let scrollTimeout = null;
|
||||
let isScrolling = false;
|
||||
|
||||
// Build a map of section IDs to their TOC links
|
||||
tocLinks.forEach(link => {
|
||||
const sectionId = decodeURIComponent(link.getAttribute('href').substring(1));
|
||||
const section = document.getElementById(sectionId);
|
||||
if (section) {
|
||||
sections.set(section, link);
|
||||
}
|
||||
});
|
||||
|
||||
// Function to scroll TOC container to keep active item in view
|
||||
const scrollTocToActive = (activeLink) => {
|
||||
if (!tocContainer || !activeLink) return;
|
||||
|
||||
const containerRect = tocContainer.getBoundingClientRect();
|
||||
const linkRect = activeLink.getBoundingClientRect();
|
||||
|
||||
// Check if the active link is outside the visible area
|
||||
if (linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom) {
|
||||
// Calculate the scroll position to center the active link
|
||||
const scrollTop = activeLink.offsetTop - (containerRect.height / 2) + (linkRect.height / 2);
|
||||
|
||||
// Use CSS smooth scrolling if available, otherwise use JS
|
||||
if ('scrollBehavior' in document.documentElement.style) {
|
||||
tocContainer.scrollTo({
|
||||
top: scrollTop,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
// Fallback for browsers that don't support smooth scrolling
|
||||
tocContainer.scrollTop = scrollTop;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function to find the section closest to the center of the viewport
|
||||
const findClosestSectionToCenter = () => {
|
||||
let closestSection = null;
|
||||
let closestDistance = Infinity;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const viewportCenter = viewportHeight / 2;
|
||||
|
||||
sections.forEach((link, section) => {
|
||||
const rect = section.getBoundingClientRect();
|
||||
// Calculate the center of the section
|
||||
const sectionCenter = rect.top + (rect.height / 2);
|
||||
// Calculate distance from the center of the viewport
|
||||
const distance = Math.abs(sectionCenter - viewportCenter);
|
||||
|
||||
// If this section is closer to the center than our current closest
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestSection = section;
|
||||
}
|
||||
});
|
||||
|
||||
return closestSection;
|
||||
};
|
||||
|
||||
// Function to update the active section
|
||||
const updateActiveSection = (activeSection) => {
|
||||
if (!activeSection) return;
|
||||
|
||||
// Update active state
|
||||
tocLinks.forEach(link => link.classList.remove('active'));
|
||||
|
||||
const activeLink = sections.get(activeSection);
|
||||
if (activeLink) {
|
||||
activeLink.classList.add('active');
|
||||
lastActiveSection = activeSection;
|
||||
|
||||
// Use a small delay before scrolling to prevent rapid changes
|
||||
if (scrollTimeout) {
|
||||
clearTimeout(scrollTimeout);
|
||||
}
|
||||
|
||||
scrollTimeout = setTimeout(() => {
|
||||
scrollTocToActive(activeLink);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to handle scroll events
|
||||
const handleScroll = () => {
|
||||
// Clear any pending scroll timeout
|
||||
if (scrollTimeout) {
|
||||
clearTimeout(scrollTimeout);
|
||||
}
|
||||
|
||||
// Find the section closest to the center of the viewport
|
||||
const closestSection = findClosestSectionToCenter();
|
||||
|
||||
// Update the active section
|
||||
updateActiveSection(closestSection);
|
||||
};
|
||||
|
||||
// Add scroll event listener with debouncing
|
||||
let scrollDebounceTimer = null;
|
||||
window.addEventListener('scroll', () => {
|
||||
isScrolling = true;
|
||||
|
||||
// Debounce the scroll event
|
||||
if (scrollDebounceTimer) {
|
||||
clearTimeout(scrollDebounceTimer);
|
||||
}
|
||||
|
||||
scrollDebounceTimer = setTimeout(() => {
|
||||
isScrolling = false;
|
||||
handleScroll();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Initial check to ensure a section is highlighted
|
||||
setTimeout(() => {
|
||||
handleScroll();
|
||||
}, 500);
|
||||
|
||||
// Smooth scroll to section when clicking TOC links
|
||||
tocLinks.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const targetId = decodeURIComponent(link.getAttribute('href').substring(1));
|
||||
const targetSection = document.getElementById(targetId);
|
||||
if (targetSection) {
|
||||
// Use our custom smooth scrolling function if available
|
||||
if (window.smoothScrollTo) {
|
||||
window.smoothScrollTo(targetSection);
|
||||
} else {
|
||||
// Fallback to standard smooth scrolling
|
||||
targetSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
// Also update the active section immediately
|
||||
const activeLink = sections.get(targetSection);
|
||||
if (activeLink) {
|
||||
tocLinks.forEach(l => l.classList.remove('active'));
|
||||
activeLink.classList.add('active');
|
||||
lastActiveSection = targetSection;
|
||||
scrollTocToActive(activeLink);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
4
layouts/partials/extend-footer.html
Normal file
4
layouts/partials/extend-footer.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{{ if .TableOfContents }}
|
||||
{{ $tocHighlight := resources.Get "js/toc-highlight.js" | js.Build | fingerprint }}
|
||||
<script src="{{ $tocHighlight.RelPermalink }}" integrity="{{ $tocHighlight.Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{ end }}
|
|
@ -1,3 +0,0 @@
|
|||
{{ if .Params.showTableOfContents | default (.Site.Params.article.showTableOfContents | default false) }}
|
||||
<script src="/js/scrollspy.js"></script>
|
||||
{{ end }}
|
Loading…
Add table
Reference in a new issue