@@ -140,9 +140,19 @@ export function TableOfContents({ headings, placement = "desktop" }: TableOfCont
140140 update ( )
141141 window . addEventListener ( "scroll" , onScroll , { passive : true } )
142142 window . addEventListener ( "resize" , onScroll )
143+ // Article body height grows as lazy <img>s load — recompute when it does.
144+ // ResizeObserver beats listening to `window.load` because images can finish
145+ // long after the load event has fired (lazy / fetchpriority='low' images).
146+ let ro : ResizeObserver | null = null
147+ const article = document . getElementById ( "article-body" )
148+ if ( article && typeof ResizeObserver !== "undefined" ) {
149+ ro = new ResizeObserver ( ( ) => onScroll ( ) )
150+ ro . observe ( article )
151+ }
143152 return ( ) => {
144153 window . removeEventListener ( "scroll" , onScroll )
145154 window . removeEventListener ( "resize" , onScroll )
155+ ro ?. disconnect ( )
146156 if ( raf ) cancelAnimationFrame ( raf )
147157 }
148158 } , [ chapters ] )
@@ -179,15 +189,25 @@ export function TableOfContents({ headings, placement = "desktop" }: TableOfCont
179189 < li
180190 key = { c . head . id }
181191 ref = { ( el ) => {
192+ // Keep the chapterRefs map in sync with the live DOM. React calls
193+ // the ref with `null` on unmount; without removing entries here
194+ // the map would accumulate forever during HMR / heading changes.
182195 if ( el ) chapterRefs . current . set ( c . head . id , el )
196+ else chapterRefs . current . delete ( c . head . id )
183197 } }
184198 className = "toc-chapter"
185- data-state = { isActive || subActive ? "active" : "upcoming" }
199+ // NB: data-state is OWNED by the scroll handler in useEffect above.
200+ // Don't drive it from React state — the two would race and the
201+ // already-read chapters would flash back to "upcoming" on every
202+ // active-id change. React only owns the auxiliary data-has-active
203+ // flag below, which controls sub-list expansion.
204+ data-has-active = { isActive || subActive ? "true" : "false" }
186205 style = { { [ "--chapter-progress" as never ] : 0 } as React . CSSProperties }
187206 >
188207 < a
189208 ref = { ( el ) => {
190209 if ( el ) linkRefs . current . set ( c . head . id , el )
210+ else linkRefs . current . delete ( c . head . id )
191211 } }
192212 href = { `#${ c . head . id } ` }
193213 className = "toc-chapter-link"
@@ -201,6 +221,7 @@ export function TableOfContents({ headings, placement = "desktop" }: TableOfCont
201221 < a
202222 ref = { ( el ) => {
203223 if ( el ) linkRefs . current . set ( s . id , el )
224+ else linkRefs . current . delete ( s . id )
204225 } }
205226 href = { `#${ s . id } ` }
206227 className = "toc-sub-link"
0 commit comments