1+
2+ type ScrollHelper = {
3+ elementId ?: string // if elementId is not provided, then the window is used
4+ onReachingBottom ?: ( ) => void
5+ onReachingTop ?: ( ) => void
6+ onLeavingBottom ?: ( ) => void
7+ onLeavingTop ?: ( ) => void
8+ onScroll ?: ( ) => void
9+ }
10+
11+ class ScrollLookup {
12+ public static active : { [ elementId : string ] : ScrollHelper } = { } ;
13+ public static lookup : {
14+ [ elementId : string ] : {
15+ scrollFunction ?: ( ) => void
16+ isTop : boolean
17+ isBottom : boolean
18+ }
19+ } = { } ;
20+ }
21+
22+
23+ export function addScroll ( scrollInfo : ScrollHelper , timeout ?: number ) {
24+ //overwrites if same id is given
25+ if ( ! scrollInfo ) return ;
26+ if ( timeout ) {
27+ setTimeout ( ( ) => addScroll ( scrollInfo ) , timeout ) ;
28+ return ;
29+ }
30+
31+ const lookupKey = scrollInfo . elementId || "@@window@@" ;
32+ const newElement = ! ( lookupKey in ScrollLookup . active ) ;
33+
34+ ScrollLookup . active [ lookupKey ] = { } ;
35+ ScrollLookup . lookup [ lookupKey ] = { isBottom : false , isTop : true } ;
36+ if ( scrollInfo . onReachingBottom ) ScrollLookup . active [ lookupKey ] . onReachingBottom = scrollInfo . onReachingBottom ;
37+ if ( scrollInfo . onReachingTop ) ScrollLookup . active [ lookupKey ] . onReachingTop = scrollInfo . onReachingTop ;
38+ if ( scrollInfo . onLeavingBottom ) ScrollLookup . active [ lookupKey ] . onLeavingBottom = scrollInfo . onLeavingBottom ;
39+ if ( scrollInfo . onLeavingTop ) ScrollLookup . active [ lookupKey ] . onLeavingTop = scrollInfo . onLeavingTop ;
40+ if ( scrollInfo . onScroll ) ScrollLookup . active [ lookupKey ] . onScroll = scrollInfo . onScroll ;
41+
42+ if ( Object . keys ( ScrollLookup . active [ lookupKey ] ) . length === 0 ) {
43+ delete ScrollLookup . active [ lookupKey ] ;
44+ delete ScrollLookup . lookup [ lookupKey ] ;
45+ return ;
46+ }
47+
48+ if ( ! newElement ) return ;
49+
50+ const [ listenerTarget , element ] = getScrollElementAndTarget ( scrollInfo . elementId ) ;
51+
52+ const scrollFunction = ( ) => {
53+ const active = ScrollLookup . active [ lookupKey ] ;
54+
55+ if ( Math . abs ( element . scrollHeight - element . scrollTop - element . clientHeight ) < 1 ) {
56+ ScrollLookup . lookup [ lookupKey ] . isBottom = true ;
57+ if ( active . onReachingBottom ) active . onReachingBottom ( ) ;
58+ } else if ( ScrollLookup . lookup [ lookupKey ] . isBottom ) {
59+ ScrollLookup . lookup [ lookupKey ] . isBottom = false ;
60+ if ( active . onLeavingBottom ) active . onLeavingBottom ( ) ;
61+ }
62+ if ( element . scrollTop < 1 ) {
63+ ScrollLookup . lookup [ lookupKey ] . isTop = true ;
64+ if ( active . onReachingTop ) active . onReachingTop ( ) ;
65+ } else if ( ScrollLookup . lookup [ lookupKey ] . isTop ) {
66+ ScrollLookup . lookup [ lookupKey ] . isTop = false ;
67+ if ( active . onLeavingTop ) active . onLeavingTop ( ) ;
68+ }
69+ if ( active . onScroll ) active . onScroll ( ) ;
70+ }
71+ ScrollLookup . lookup [ lookupKey ] . scrollFunction = scrollFunction ;
72+
73+ listenerTarget . addEventListener ( "scroll" , scrollFunction , false ) ;
74+ }
75+
76+ function getScrollElementAndTarget ( elementId ?: string ) : [ HTMLElement | Window , HTMLElement ] {
77+ if ( ! elementId ) return [ window , document . documentElement ] ;
78+ const element = document . getElementById ( elementId ) ;
79+ return [ element , element ] ;
80+ }
81+
82+ export function removeScroll ( elementId ?: string ) {
83+ const lookupKey = elementId || "@@window@@" ;
84+ if ( ! ( lookupKey in ScrollLookup . active ) ) return ;
85+ const [ listenerTarget , element ] = getScrollElementAndTarget ( elementId ) ;
86+ if ( listenerTarget ) listenerTarget . removeEventListener ( "scroll" , ScrollLookup . lookup [ lookupKey ] . scrollFunction , false ) ;
87+ delete ScrollLookup . active [ lookupKey ] ;
88+ delete ScrollLookup . lookup [ lookupKey ] ;
89+
90+ }
91+
92+ export function scrollElementIntoView ( elementId : string , timeout ?: number ) {
93+ if ( timeout ) {
94+ setTimeout ( ( ) => scrollElementIntoView ( elementId ) , timeout ) ;
95+ return ;
96+ }
97+
98+ const element = document . getElementById ( elementId ) ;
99+ if ( ! element ) {
100+ throw new Error ( "scrollElementIntoView elementId not found" ) ;
101+ }
102+ element . scrollIntoView ( { behavior : "smooth" , block : "start" } ) ;
103+ }
0 commit comments