@@ -37,11 +37,14 @@ export class ScrollSpyController implements ReactiveController {
3737 static {
3838 if ( ! isServer ) {
3939 addEventListener ( 'scroll' , ( ) => {
40- if ( Math . round ( window . innerHeight + window . scrollY ) >= document . body . scrollHeight ) {
41- this . #instances. forEach ( ssc => {
42- ssc . #setActive( ssc . #linkChildren. at ( - 1 ) ) ;
43- } ) ;
44- }
40+ this . #instances. forEach ( ssc => {
41+ ssc . #reconcile( ) ;
42+ } ) ;
43+ } , { passive : true } ) ;
44+ addEventListener ( 'scrollend' , ( ) => {
45+ this . #instances. forEach ( ssc => {
46+ ssc . #reconcile( ) ;
47+ } ) ;
4548 } , { passive : true } ) ;
4649 addEventListener ( 'hashchange' , ( ) => {
4750 this . #instances. forEach ( ssc => {
@@ -72,9 +75,10 @@ export class ScrollSpyController implements ReactiveController {
7275
7376 #threshold: number | number [ ] ;
7477
75- #intersectingTargets = new Set < Element > ( ) ;
78+ #intersectionEntries = new Set < IntersectionObserverEntry > ( ) ;
7679
7780 #linkTargetMap = new Map < Element , Element | null > ( ) ;
81+ #targetLinkMap = new Map < Element , Element | null > ( ) ;
7882
7983 #getRootNode: ( ) => Node | null ;
8084
@@ -148,6 +152,24 @@ export class ScrollSpyController implements ReactiveController {
148152
149153 #initializing = true ;
150154
155+ #reconcile( ) {
156+ const { scrollY, innerHeight } = window ;
157+ let link : Element | null | undefined = null ;
158+ if ( scrollY === 0 ) {
159+ link = this . #linkChildren. at ( 0 ) ;
160+ } else if ( Math . round ( innerHeight + scrollY ) >= document . body . scrollHeight ) {
161+ link = this . #linkChildren. at ( - 1 ) ;
162+ } else {
163+ const [ entry ] = [ ...this . #intersectionEntries] . sort ( ( a , b ) => {
164+ return b . boundingClientRect . y - a . boundingClientRect . y ;
165+ } ) ;
166+ link = this . #targetLinkMap. get ( entry . target ) ;
167+ }
168+ if ( link ) {
169+ this . #setActive( link ) ;
170+ }
171+ }
172+
151173 async #initIo( ) {
152174 const rootNode = this . #getRootNode( ) ;
153175 if ( rootNode instanceof Document || rootNode instanceof ShadowRoot ) {
@@ -160,6 +182,7 @@ export class ScrollSpyController implements ReactiveController {
160182 if ( target ) {
161183 this . #io?. observe ( target ) ;
162184 this . #linkTargetMap. set ( link , target ) ;
185+ this . #targetLinkMap. set ( target , link ) ;
163186 }
164187 }
165188 }
@@ -209,32 +232,19 @@ export class ScrollSpyController implements ReactiveController {
209232 this . #markPassed( link , boundingClientRect . top < intersectionRect . top ) ;
210233 }
211234 }
212- const link = [ ...this . #passedLinks] ;
213- const last = link . at ( - 1 ) ;
214- this . #setActive( last ?? this . #linkChildren. at ( 0 ) ) ;
215235 }
216236 this . #intersected = true ;
217- this . #intersectingTargets . clear ( ) ;
237+ this . #intersectionEntries . clear ( ) ;
218238 for ( const entry of entries ) {
219239 if ( entry . isIntersecting ) {
220- this . #intersectingTargets . add ( entry . target ) ;
240+ this . #intersectionEntries . add ( entry ) ;
221241 }
222242 }
223243 if ( this . #initializing) {
224- const ints = entries ?. filter ( x => x . isIntersecting ) ?? [ ] ;
225- if ( this . #intersectingTargets. size > 0 ) {
226- const [ { target = null } = { } ] = ints ;
227- const { id } = target ?? { } ;
228- if ( id ) {
229- const link = this . #linkChildren. find ( link => this . #getHash( link ) === `#${ id } ` ) ;
230- if ( link ) {
231- this . #setActive( link ) ;
232- }
233- }
234- }
235244 this . #initializing = false ;
236245 }
237246 this . #onIntersection?.( ) ;
247+ this . #reconcile( ) ;
238248 }
239249
240250 /**
0 commit comments