Enhanced TableOfContents Component with Improved Scroll Behavior and Progress Tracking
Summary
Enhanced the TableOfContents component with improved scroll behavior, progress bar functionality, and better user experience through native scrolling and proper scroll margin handling.
Why Care
These improvements provide a more responsive and accurate table of contents experience, with real-time progress tracking and smoother navigation. The changes ensure proper scroll positioning that accounts for fixed headers and provides better visual feedback to users about their reading progress.
Implementation
Changes Made
- File Modified:
site/src/components/markdown/TableOfContents.astro
- Scroll Behavior: Replaced custom scroll calculation with native
scrollIntoView()
method - Progress Bar: Updated to track scroll position within
.collection-reader-pane
instead of window - Scroll Margin: Implemented dynamic scroll margin application via JavaScript for proper header positioning
- Event Listeners: Enhanced scroll event handling to target specific content containers
- Collapsed State: Increased collapsed TOC width from 48px to 60px for better usability
Technical Details
Scroll Behavior Improvements
javascript
// Before: Custom scroll calculation with manual offset
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
// After: Native scrollIntoView with dynamic margin
element.style.scrollMarginTop = '150px';
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
Progress Bar Container Targeting
javascript
// Updated to use collection reader pane instead of window
const collectionReaderPane = document.querySelector('.collection-reader-pane');
if (collectionReaderPane) {
const scrollTop = collectionReaderPane.scrollTop;
const scrollHeight = collectionReaderPane.scrollHeight;
const clientHeight = collectionReaderPane.clientHeight;
const scrollPercentage = Math.min(100, Math.max(0, (scrollTop / (scrollHeight - clientHeight)) * 100));
}
Scroll Event Listener Enhancement
javascript
// Primary: Collection reader pane scroll tracking
const collectionReaderPane = document.querySelector('.collection-reader-pane');
if (collectionReaderPane) {
collectionReaderPane.addEventListener('scroll', () => {
// Update active link and progress bar
}, { passive: true });
} else {
// Fallback to window scroll
window.addEventListener('scroll', () => {
// Fallback implementation
}, { passive: true });
}
CSS Updates
css
/* Increased collapsed TOC width for better usability */
.toc-sidebar.collapsed {
width: 60px;
min-width: 60px;
max-width: 60px;
}
/* Added scroll margin for headings */
.heading-with-copy,
h1, h2, h3, h4, h5, h6 {
scroll-margin-top: 500px;
}
Integration Points
- Content Container: Now properly integrates with
.collection-reader-pane
scroll behavior - Header Positioning: Accounts for fixed headers through dynamic scroll margin application
- Progress Tracking: Provides real-time feedback on reading progress within the content area
- Responsive Design: Maintains compatibility with existing responsive breakpoints
Documentation
- Native Scrolling: Uses browser-native
scrollIntoView()
for better performance and reliability - Scroll Margin: JavaScript-applied scroll margin ensures proper positioning regardless of CSS loading
- Event Handling: Robust fallback system ensures functionality even if content container structure changes
- Performance: Uses
requestAnimationFrame
for smooth scroll event handling without performance impact
Technical Decisions
- Native vs Custom Scrolling: Chose native
scrollIntoView()
over custom calculations for better browser compatibility and performance - JavaScript Scroll Margin: Applied scroll margin via JavaScript instead of CSS due to timing and specificity issues
- Container-Specific Tracking: Targeted
.collection-reader-pane
for more accurate progress tracking - Fallback Strategy: Implemented window scroll fallback to ensure functionality in all scenarios
Performance Impact
- Positive: Reduced scroll calculation overhead by using native browser methods
- Positive: More efficient event handling with container-specific listeners
- Neutral: Minimal impact from additional progress bar updates during scroll
- Positive: Better user experience with smoother scrolling and accurate positioning