@@ -124,40 +124,49 @@ export function useActiveTocItem(toc: Ref<TocItem[]>) {
124124 behavior : 'smooth' ,
125125 } )
126126
127- // Monitor scroll until it settles, THEN update URL hash
128- let lastScrollY = window . scrollY
129- let stableFrames = 0
130- let rafId : number | null = null
131- const STABLE_THRESHOLD = 5 // Number of frames with no movement to consider settled
132-
133- const checkScrollSettled = ( ) => {
134- const currentScrollY = window . scrollY
135-
136- if ( Math . abs ( currentScrollY - lastScrollY ) < 1 ) {
137- stableFrames ++
138- if ( stableFrames >= STABLE_THRESHOLD ) {
139- // Scroll has settled - NOW update URL hash (won't trigger native scroll)
140- history . replaceState ( null , '' , `#${ id } ` )
141- setupObserver ( )
142- scrollCleanup = null
143- return
127+ const handleScrollEnd = ( ) => {
128+ history . replaceState ( null , '' , `#${ id } ` )
129+ setupObserver ( )
130+ scrollCleanup = null
131+ }
132+
133+ // Check for scrollend support (Chrome 114+, Firefox 109+, Safari 18+)
134+ const supportsScrollEnd = 'onscrollend' in window
135+
136+ if ( supportsScrollEnd ) {
137+ window . addEventListener ( 'scrollend' , handleScrollEnd , { once : true } )
138+ scrollCleanup = ( ) => window . removeEventListener ( 'scrollend' , handleScrollEnd )
139+ } else {
140+ // Fallback: use RAF polling for older browsers
141+ let lastScrollY = window . scrollY
142+ let stableFrames = 0
143+ let rafId : number | null = null
144+ const STABLE_THRESHOLD = 5 // Number of frames with no movement to consider settled
145+
146+ const checkScrollSettled = ( ) => {
147+ const currentScrollY = window . scrollY
148+
149+ if ( Math . abs ( currentScrollY - lastScrollY ) < 1 ) {
150+ stableFrames ++
151+ if ( stableFrames >= STABLE_THRESHOLD ) {
152+ handleScrollEnd ( )
153+ return
154+ }
155+ } else {
156+ stableFrames = 0
144157 }
145- } else {
146- stableFrames = 0
158+
159+ lastScrollY = currentScrollY
160+ rafId = requestAnimationFrame ( checkScrollSettled )
147161 }
148162
149- lastScrollY = currentScrollY
150163 rafId = requestAnimationFrame ( checkScrollSettled )
151- }
152164
153- // Start monitoring
154- rafId = requestAnimationFrame ( checkScrollSettled )
155-
156- // Store cleanup function
157- scrollCleanup = ( ) => {
158- if ( rafId !== null ) {
159- cancelAnimationFrame ( rafId )
160- rafId = null
165+ scrollCleanup = ( ) => {
166+ if ( rafId !== null ) {
167+ cancelAnimationFrame ( rafId )
168+ rafId = null
169+ }
161170 }
162171 }
163172
@@ -169,7 +178,7 @@ export function useActiveTocItem(toc: Ref<TocItem[]>) {
169178 history . replaceState ( null , '' , `#${ id } ` )
170179 setupObserver ( )
171180 }
172- } , 1500 )
181+ } , 1000 )
173182 }
174183
175184 // Set up observer when TOC changes
0 commit comments