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) {
|
.toc:has(#TableOfContents) {
|
||||||
background-color: rgba(25, 25, 35, 0.45);
|
background-color: rgba(25, 25, 35, 0.3);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
.background-container {
|
.background-container {
|
||||||
|
@ -107,4 +110,45 @@ header button[id^="search-button"]:hover {
|
||||||
100% {
|
100% {
|
||||||
backdrop-filter: blur(8px) hue-rotate(-10deg);
|
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