@@ -359,7 +359,6 @@ export class Virtualizer<
359359 scrollElement : TScrollElement | null = null
360360 targetWindow : ( Window & typeof globalThis ) | null = null
361361 isScrolling = false
362- private scrollToIndexTimeoutId : number | null = null
363362 measurementsCache : Array < VirtualItem > = [ ]
364363 private itemSizeCache = new Map < Key , number > ( )
365364 private pendingMeasuredCacheIndexes : Array < number > = [ ]
@@ -904,7 +903,7 @@ export class Virtualizer<
904903 toOffset -= size
905904 }
906905
907- const maxOffset = this . getTotalSize ( ) - size
906+ const maxOffset = this . getTotalSize ( ) + this . options . scrollMargin - size
908907
909908 return Math . max ( Math . min ( maxOffset , toOffset ) , 0 )
910909 }
@@ -943,19 +942,10 @@ export class Virtualizer<
943942
944943 private isDynamicMode = ( ) => this . elementsCache . size > 0
945944
946- private cancelScrollToIndex = ( ) => {
947- if ( this . scrollToIndexTimeoutId !== null && this . targetWindow ) {
948- this . targetWindow . clearTimeout ( this . scrollToIndexTimeoutId )
949- this . scrollToIndexTimeoutId = null
950- }
951- }
952-
953945 scrollToOffset = (
954946 toOffset : number ,
955947 { align = 'start' , behavior } : ScrollToOffsetOptions = { } ,
956948 ) => {
957- this . cancelScrollToIndex ( )
958-
959949 if ( behavior === 'smooth' && this . isDynamicMode ( ) ) {
960950 console . warn (
961951 'The `smooth` scroll behavior is not fully supported with dynamic size.' ,
@@ -972,50 +962,55 @@ export class Virtualizer<
972962 index : number ,
973963 { align : initialAlign = 'auto' , behavior } : ScrollToIndexOptions = { } ,
974964 ) => {
975- index = Math . max ( 0 , Math . min ( index , this . options . count - 1 ) )
976-
977- this . cancelScrollToIndex ( )
978-
979965 if ( behavior === 'smooth' && this . isDynamicMode ( ) ) {
980966 console . warn (
981967 'The `smooth` scroll behavior is not fully supported with dynamic size.' ,
982968 )
983969 }
984970
985- const offsetAndAlign = this . getOffsetForIndex ( index , initialAlign )
986- if ( ! offsetAndAlign ) return
987-
988- const [ offset , align ] = offsetAndAlign
989-
990- this . _scrollToOffset ( offset , { adjustments : undefined , behavior } )
991-
992- if ( behavior !== 'smooth' && this . isDynamicMode ( ) && this . targetWindow ) {
993- this . scrollToIndexTimeoutId = this . targetWindow . setTimeout ( ( ) => {
994- this . scrollToIndexTimeoutId = null
971+ index = Math . max ( 0 , Math . min ( index , this . options . count - 1 ) )
995972
996- const elementInDOM = this . elementsCache . has (
997- this . options . getItemKey ( index ) ,
998- )
973+ let attempts = 0
974+ const maxAttempts = 10
999975
1000- if ( elementInDOM ) {
1001- const result = this . getOffsetForIndex ( index , align )
1002- if ( ! result ) return
1003- const [ latestOffset ] = result
976+ const tryScroll = ( currentAlign : ScrollAlignment ) => {
977+ const offsetInfo = this . getOffsetForIndex ( index , currentAlign )
978+ if ( ! offsetInfo ) {
979+ scheduleRetry ( currentAlign )
980+ return
981+ }
982+ const [ offset , align ] = offsetInfo
983+ this . _scrollToOffset ( offset , { adjustments : undefined , behavior } )
984+
985+ requestAnimationFrame ( ( ) => {
986+ const currentOffset = this . getScrollOffset ( )
987+ const afterInfo = this . getOffsetForIndex ( index , align )
988+ if ( ! afterInfo ) {
989+ scheduleRetry ( align )
990+ return
991+ }
1004992
1005- const currentScrollOffset = this . getScrollOffset ( )
1006- if ( ! approxEqual ( latestOffset , currentScrollOffset ) ) {
1007- this . scrollToIndex ( index , { align, behavior } )
1008- }
1009- } else {
1010- this . scrollToIndex ( index , { align, behavior } )
993+ if ( ! approxEqual ( afterInfo [ 0 ] , currentOffset ) ) {
994+ scheduleRetry ( align )
1011995 }
1012996 } )
1013997 }
998+
999+ function scheduleRetry ( align : ScrollAlignment ) {
1000+ attempts ++
1001+ if ( attempts < maxAttempts ) {
1002+ requestAnimationFrame ( ( ) => tryScroll ( align ) )
1003+ } else {
1004+ console . warn (
1005+ `Failed to scroll to index ${ index } after ${ maxAttempts } attempts.` ,
1006+ )
1007+ }
1008+ }
1009+
1010+ tryScroll ( initialAlign )
10141011 }
10151012
10161013 scrollBy = ( delta : number , { behavior } : ScrollToOffsetOptions = { } ) => {
1017- this . cancelScrollToIndex ( )
1018-
10191014 if ( behavior === 'smooth' && this . isDynamicMode ( ) ) {
10201015 console . warn (
10211016 'The `smooth` scroll behavior is not fully supported with dynamic size.' ,
0 commit comments