Skip to content

Commit 998fa25

Browse files
committed
Fix ToC active state not updating on scroll
The scroll listener was attached to the container element, but the page uses window-based scrolling. Changed to listen on window instead.
1 parent a2ebe1b commit 998fa25

1 file changed

Lines changed: 14 additions & 20 deletions

File tree

src/components/TableOfContents.jsx

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ const TableOfContents = ({ containerRef, width, collapsed, onToggle, onItemMouse
102102
useEffect(() => {
103103
if (!containerRef?.current || sections.length === 0) return;
104104

105-
const container = containerRef.current;
106105
let rafId = null;
107106

108107
const updateActiveSection = () => {
@@ -112,38 +111,33 @@ const TableOfContents = ({ containerRef, width, collapsed, onToggle, onItemMouse
112111
}
113112

114113
rafId = requestAnimationFrame(() => {
115-
const containerRect = container.getBoundingClientRect();
116-
const containerTop = containerRect.top;
117-
const containerHeight = containerRect.height;
118-
114+
const viewportHeight = window.innerHeight;
115+
119116
// Find the first heading that's clearly visible in the viewport
120117
let activeId = null;
121118
let fallbackId = null;
122119
let minDistance = Infinity;
123-
120+
124121
for (const section of sections) {
125122
if (!section.element) continue;
126-
123+
127124
const rect = section.element.getBoundingClientRect();
128-
const elementTop = rect.top;
129-
130-
// Distance from top of container (negative = above viewport)
131-
const distanceFromTop = elementTop - containerTop;
132-
125+
const distanceFromTop = rect.top;
126+
133127
// If heading is in the upper portion of viewport (top 60%), make it active
134-
if (distanceFromTop >= 0 && distanceFromTop <= containerHeight * 0.6) {
128+
if (distanceFromTop >= 0 && distanceFromTop <= viewportHeight * 0.6) {
135129
activeId = section.id;
136130
break; // First one wins - this is a clear active section
137131
}
138-
132+
139133
// Track closest heading for potential fallback, but only if reasonably close
140134
const absoluteDistance = Math.abs(distanceFromTop);
141-
if (absoluteDistance < minDistance && absoluteDistance < containerHeight * 0.8) {
135+
if (absoluteDistance < minDistance && absoluteDistance < viewportHeight * 0.8) {
142136
minDistance = absoluteDistance;
143137
fallbackId = section.id;
144138
}
145139
}
146-
140+
147141
// Only update if we found a clearly active section, or if we don't have any active section yet
148142
if (activeId) {
149143
// Clear active section found
@@ -158,14 +152,14 @@ const TableOfContents = ({ containerRef, width, collapsed, onToggle, onItemMouse
158152
});
159153
};
160154

161-
// Listen to scroll events
162-
container.addEventListener('scroll', updateActiveSection, { passive: true });
163-
155+
// Listen to scroll events on window (page uses window scrolling, not container scrolling)
156+
window.addEventListener('scroll', updateActiveSection, { passive: true });
157+
164158
// Initial update
165159
updateActiveSection();
166160

167161
return () => {
168-
container.removeEventListener('scroll', updateActiveSection);
162+
window.removeEventListener('scroll', updateActiveSection);
169163
if (rafId) {
170164
cancelAnimationFrame(rafId);
171165
}

0 commit comments

Comments
 (0)