1
- import React , { Component } from ' react' ;
2
- import PropTypes from ' prop-types' ;
1
+ import React , { Component } from " react" ;
2
+ import PropTypes from " prop-types" ;
3
3
import throttle from './utils/throttle' ;
4
4
5
5
export default class InfiniteScroll extends Component {
6
- constructor ( props ) {
6
+ constructor ( props ) {
7
7
super ( ) ;
8
8
this . state = {
9
9
showLoader : false ,
@@ -20,37 +20,43 @@ export default class InfiniteScroll extends Component {
20
20
this . maxPullDownDistance = 0 ;
21
21
22
22
this . onScrollListener = this . onScrollListener . bind ( this ) ;
23
- this . throttledOnScrollListener = throttle ( this . onScrollListener , 150 ) . bind ( this ) ;
23
+ this . throttledOnScrollListener = throttle ( this . onScrollListener , 150 ) . bind (
24
+ this
25
+ ) ;
24
26
this . onStart = this . onStart . bind ( this ) ;
25
27
this . onMove = this . onMove . bind ( this ) ;
26
28
this . onEnd = this . onEnd . bind ( this ) ;
29
+ this . getScrollableTarget = this . getScrollableTarget . bind ( this ) ;
27
30
}
28
31
29
- componentDidMount ( ) {
30
- this . el = this . props . height ? this . _infScroll : this . props . scrollableTarget || window ;
31
- this . el . addEventListener ( 'scroll' , this . throttledOnScrollListener ) ;
32
+ componentDidMount ( ) {
33
+ this . _scrollableNode = this . getScrollableTarget ( ) ;
34
+ this . el = this . props . height
35
+ ? this . _infScroll
36
+ : this . _scrollableNode || window ;
37
+ this . el . addEventListener ( "scroll" , this . throttledOnScrollListener ) ;
32
38
33
39
if (
34
- typeof this . props . initialScrollY === ' number' &&
40
+ typeof this . props . initialScrollY === " number" &&
35
41
this . el . scrollHeight > this . props . initialScrollY
36
42
) {
37
43
this . el . scrollTo ( 0 , this . props . initialScrollY ) ;
38
44
}
39
45
40
46
if ( this . props . pullDownToRefresh ) {
41
- this . el . addEventListener ( ' touchstart' , this . onStart ) ;
42
- this . el . addEventListener ( ' touchmove' , this . onMove ) ;
43
- this . el . addEventListener ( ' touchend' , this . onEnd ) ;
47
+ this . el . addEventListener ( " touchstart" , this . onStart ) ;
48
+ this . el . addEventListener ( " touchmove" , this . onMove ) ;
49
+ this . el . addEventListener ( " touchend" , this . onEnd ) ;
44
50
45
- this . el . addEventListener ( ' mousedown' , this . onStart ) ;
46
- this . el . addEventListener ( ' mousemove' , this . onMove ) ;
47
- this . el . addEventListener ( ' mouseup' , this . onEnd ) ;
51
+ this . el . addEventListener ( " mousedown" , this . onStart ) ;
52
+ this . el . addEventListener ( " mousemove" , this . onMove ) ;
53
+ this . el . addEventListener ( " mouseup" , this . onEnd ) ;
48
54
49
55
// get BCR of pullDown element to position it above
50
56
this . maxPullDownDistance = this . _pullDown . firstChild . getBoundingClientRect ( ) . height ;
51
57
this . forceUpdate ( ) ;
52
58
53
- if ( typeof this . props . refreshFunction !== ' function' ) {
59
+ if ( typeof this . props . refreshFunction !== " function" ) {
54
60
throw new Error (
55
61
`Mandatory prop "refreshFunction" missing.
56
62
Pull Down To Refresh functionality will not work
@@ -60,22 +66,21 @@ export default class InfiniteScroll extends Component {
60
66
}
61
67
}
62
68
63
- componentWillUnmount ( ) {
64
- this . el . removeEventListener ( ' scroll' , this . throttledOnScrollListener ) ;
69
+ componentWillUnmount ( ) {
70
+ this . el . removeEventListener ( " scroll" , this . throttledOnScrollListener ) ;
65
71
66
72
if ( this . props . pullDownToRefresh ) {
67
- this . el . removeEventListener ( ' touchstart' , this . onStart ) ;
68
- this . el . removeEventListener ( ' touchmove' , this . onMove ) ;
69
- this . el . removeEventListener ( ' touchend' , this . onEnd ) ;
73
+ this . el . removeEventListener ( " touchstart" , this . onStart ) ;
74
+ this . el . removeEventListener ( " touchmove" , this . onMove ) ;
75
+ this . el . removeEventListener ( " touchend" , this . onEnd ) ;
70
76
71
- this . el . removeEventListener ( ' mousedown' , this . onStart ) ;
72
- this . el . removeEventListener ( ' mousemove' , this . onMove ) ;
73
- this . el . removeEventListener ( ' mouseup' , this . onEnd ) ;
77
+ this . el . removeEventListener ( " mousedown" , this . onStart ) ;
78
+ this . el . removeEventListener ( " mousemove" , this . onMove ) ;
79
+ this . el . removeEventListener ( " mouseup" , this . onEnd ) ;
74
80
}
75
81
}
76
82
77
- componentWillReceiveProps ( props ) {
78
-
83
+ componentWillReceiveProps ( props ) {
79
84
// do nothing when dataLength is unchanged
80
85
if ( this . props . dataLength === props . dataLength ) return ;
81
86
@@ -87,25 +92,39 @@ export default class InfiniteScroll extends Component {
87
92
} ) ;
88
93
}
89
94
90
- onStart ( evt ) {
95
+ getScrollableTarget ( ) {
96
+ if ( this . props . scrollableTarget instanceof HTMLElement ) return this . props . scrollableTarget ;
97
+ if ( typeof this . props . scrollableTarget === 'string' ) {
98
+ return document . getElementById ( this . props . scrollableTarget ) ;
99
+ }
100
+ if ( this . props . scrollableTarget === null ) {
101
+ console . warn ( `You are trying to pass scrollableTarget but it is null. This might
102
+ happen because the element may not have been added to DOM yet.
103
+ See https://github.com/ankeetmaini/react-infinite-scroll-component/issues/59 for more info.
104
+ ` ) ;
105
+ }
106
+ return null ;
107
+ }
108
+
109
+ onStart ( evt ) {
91
110
if ( this . state . lastScrollTop ) return ;
92
111
93
112
this . dragging = true ;
94
113
this . startY = evt . pageY || evt . touches [ 0 ] . pageY ;
95
114
this . currentY = this . startY ;
96
115
97
- this . _infScroll . style . willChange = ' transform' ;
116
+ this . _infScroll . style . willChange = " transform" ;
98
117
this . _infScroll . style . transition = `transform 0.2s cubic-bezier(0,0,0.31,1)` ;
99
118
}
100
119
101
- onMove ( evt ) {
120
+ onMove ( evt ) {
102
121
if ( ! this . dragging ) return ;
103
122
this . currentY = evt . pageY || evt . touches [ 0 ] . pageY ;
104
123
105
124
// user is scrolling down to up
106
125
if ( this . currentY < this . startY ) return ;
107
126
108
- if ( ( this . currentY - this . startY ) >= this . props . pullDownToRefreshThreshold ) {
127
+ if ( this . currentY - this . startY >= this . props . pullDownToRefreshThreshold ) {
109
128
this . setState ( {
110
129
pullToRefreshThresholdBreached : true
111
130
} ) ;
@@ -114,11 +133,12 @@ export default class InfiniteScroll extends Component {
114
133
// so you can drag upto 1.5 times of the maxPullDownDistance
115
134
if ( this . currentY - this . startY > this . maxPullDownDistance * 1.5 ) return ;
116
135
117
- this . _infScroll . style . overflow = 'visible' ;
118
- this . _infScroll . style . transform = `translate3d(0px, ${ this . currentY - this . startY } px, 0px)` ;
136
+ this . _infScroll . style . overflow = "visible" ;
137
+ this . _infScroll . style . transform = `translate3d(0px, ${ this . currentY -
138
+ this . startY } px, 0px)`;
119
139
}
120
140
121
- onEnd ( evt ) {
141
+ onEnd ( evt ) {
122
142
this . startY = 0 ;
123
143
this . currentY = 0 ;
124
144
@@ -129,29 +149,36 @@ export default class InfiniteScroll extends Component {
129
149
}
130
150
131
151
requestAnimationFrame ( ( ) => {
132
- this . _infScroll . style . overflow = ' auto' ;
133
- this . _infScroll . style . transform = ' none' ;
134
- this . _infScroll . style . willChange = ' none' ;
152
+ this . _infScroll . style . overflow = " auto" ;
153
+ this . _infScroll . style . transform = " none" ;
154
+ this . _infScroll . style . willChange = " none" ;
135
155
} ) ;
136
156
}
137
157
138
- isElementAtBottom ( target , scrollThreshold = 0.8 ) {
139
- const clientHeight = ( target === document . body || target === document . documentElement )
140
- ? window . screen . availHeight : target . clientHeight ;
158
+ isElementAtBottom ( target , scrollThreshold = 0.8 ) {
159
+ const clientHeight =
160
+ target === document . body || target === document . documentElement
161
+ ? window . screen . availHeight
162
+ : target . clientHeight ;
141
163
142
- return ( target . scrollTop + clientHeight ) >= scrollThreshold * target . scrollHeight ;
164
+ return (
165
+ target . scrollTop + clientHeight >= scrollThreshold * target . scrollHeight
166
+ ) ;
143
167
}
144
168
145
- onScrollListener ( event ) {
146
- if ( typeof this . props . onScroll === ' function' ) {
169
+ onScrollListener ( event ) {
170
+ if ( typeof this . props . onScroll === " function" ) {
147
171
// Execute this callback in next tick so that it does not affect the
148
172
// functionality of the library.
149
173
setTimeout ( ( ) => this . props . onScroll ( event ) , 0 ) ;
150
174
}
151
175
152
- let target = this . props . height || this . props . scrollableTarget
153
- ? event . target
154
- : ( document . documentElement . scrollTop ? document . documentElement : document . body ) ;
176
+ let target =
177
+ this . props . height || this . _scrollableNode
178
+ ? event . target
179
+ : document . documentElement . scrollTop
180
+ ? document . documentElement
181
+ : document . body ;
155
182
156
183
// return immediately if the action has already been triggered,
157
184
// prevents multiple triggers.
@@ -161,43 +188,49 @@ export default class InfiniteScroll extends Component {
161
188
162
189
// call the `next` function in the props to trigger the next data fetch
163
190
if ( atBottom && this . props . hasMore ) {
164
- this . setState ( { actionTriggered : true , showLoader : true } ) ;
191
+ this . setState ( { actionTriggered : true , showLoader : true } ) ;
165
192
this . props . next ( ) ;
166
193
}
167
- this . setState ( { lastScrollTop : target . scrollTop } ) ;
194
+ this . setState ( { lastScrollTop : target . scrollTop } ) ;
168
195
}
169
196
170
- render ( ) {
197
+ render ( ) {
171
198
const style = {
172
- height : this . props . height || ' auto' ,
173
- overflow : ' auto' ,
174
- WebkitOverflowScrolling : ' touch' ,
199
+ height : this . props . height || " auto" ,
200
+ overflow : " auto" ,
201
+ WebkitOverflowScrolling : " touch" ,
175
202
...this . props . style
176
203
} ;
177
- const hasChildren = this . props . hasChildren || ! ! ( this . props . children && this . props . children . length ) ;
204
+ const hasChildren =
205
+ this . props . hasChildren ||
206
+ ! ! ( this . props . children && this . props . children . length ) ;
178
207
179
208
// because heighted infiniteScroll visualy breaks
180
209
// on drag down as overflow becomes visible
181
- const outerDivStyle = ( this . props . pullDownToRefresh && this . props . height )
182
- ? { overflow : 'auto' } : { } ;
210
+ const outerDivStyle =
211
+ this . props . pullDownToRefresh && this . props . height
212
+ ? { overflow : "auto" }
213
+ : { } ;
183
214
return (
184
215
< div style = { outerDivStyle } >
185
216
< div
186
- className = ' infinite-scroll-component'
187
- ref = { infScroll => this . _infScroll = infScroll }
217
+ className = " infinite-scroll-component"
218
+ ref = { infScroll => ( this . _infScroll = infScroll ) }
188
219
style = { style }
189
220
>
190
221
{ this . props . pullDownToRefresh && (
191
222
< div
192
- style = { { position : ' relative' } }
193
- ref = { pullDown => this . _pullDown = pullDown }
223
+ style = { { position : " relative" } }
224
+ ref = { pullDown => ( this . _pullDown = pullDown ) }
194
225
>
195
- < div style = { {
196
- position : 'absolute' ,
197
- left : 0 ,
198
- right : 0 ,
199
- top : ( - 1 * this . maxPullDownDistance ) ,
200
- } } >
226
+ < div
227
+ style = { {
228
+ position : "absolute" ,
229
+ left : 0 ,
230
+ right : 0 ,
231
+ top : - 1 * this . maxPullDownDistance
232
+ } }
233
+ >
201
234
{ ! this . state . pullToRefreshThresholdBreached &&
202
235
this . props . pullDownToRefreshContent }
203
236
{ this . state . pullToRefreshThresholdBreached &&
@@ -206,7 +239,9 @@ export default class InfiniteScroll extends Component {
206
239
</ div >
207
240
) }
208
241
{ this . props . children }
209
- { ! this . state . showLoader && ! hasChildren && this . props . hasMore &&
242
+ { ! this . state . showLoader &&
243
+ ! hasChildren &&
244
+ this . props . hasMore &&
210
245
this . props . loader }
211
246
{ this . state . showLoader && this . props . hasMore && this . props . loader }
212
247
{ ! this . props . hasMore && this . props . endMessage }
@@ -221,7 +256,7 @@ InfiniteScroll.defaultProps = {
221
256
releaseToRefreshContent : < h3 > Release to refresh</ h3 > ,
222
257
pullDownToRefreshThreshold : 100 ,
223
258
disableBrowserPullToRefresh : true
224
- }
259
+ } ;
225
260
226
261
InfiniteScroll . propTypes = {
227
262
next : PropTypes . func ,
@@ -240,5 +275,5 @@ InfiniteScroll.propTypes = {
240
275
pullDownToRefreshThreshold : PropTypes . number ,
241
276
refreshFunction : PropTypes . func ,
242
277
onScroll : PropTypes . func ,
243
- dataLength : PropTypes . number . isRequired ,
278
+ dataLength : PropTypes . number . isRequired
244
279
} ;
0 commit comments