Skip to content

Commit 912d6ab

Browse files
authored
Fixes #59 scrollable target null. (#60)
1 parent 07ee9fa commit 912d6ab

File tree

1 file changed

+101
-66
lines changed

1 file changed

+101
-66
lines changed

app/index.js

Lines changed: 101 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import React, {Component} from 'react';
2-
import PropTypes from 'prop-types';
1+
import React, { Component } from "react";
2+
import PropTypes from "prop-types";
33
import throttle from './utils/throttle';
44

55
export default class InfiniteScroll extends Component {
6-
constructor (props) {
6+
constructor(props) {
77
super();
88
this.state = {
99
showLoader: false,
@@ -20,37 +20,43 @@ export default class InfiniteScroll extends Component {
2020
this.maxPullDownDistance = 0;
2121

2222
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+
);
2426
this.onStart = this.onStart.bind(this);
2527
this.onMove = this.onMove.bind(this);
2628
this.onEnd = this.onEnd.bind(this);
29+
this.getScrollableTarget = this.getScrollableTarget.bind(this);
2730
}
2831

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);
3238

3339
if (
34-
typeof this.props.initialScrollY === 'number' &&
40+
typeof this.props.initialScrollY === "number" &&
3541
this.el.scrollHeight > this.props.initialScrollY
3642
) {
3743
this.el.scrollTo(0, this.props.initialScrollY);
3844
}
3945

4046
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);
4450

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);
4854

4955
// get BCR of pullDown element to position it above
5056
this.maxPullDownDistance = this._pullDown.firstChild.getBoundingClientRect().height;
5157
this.forceUpdate();
5258

53-
if (typeof this.props.refreshFunction !== 'function') {
59+
if (typeof this.props.refreshFunction !== "function") {
5460
throw new Error(
5561
`Mandatory prop "refreshFunction" missing.
5662
Pull Down To Refresh functionality will not work
@@ -60,22 +66,21 @@ export default class InfiniteScroll extends Component {
6066
}
6167
}
6268

63-
componentWillUnmount () {
64-
this.el.removeEventListener('scroll', this.throttledOnScrollListener);
69+
componentWillUnmount() {
70+
this.el.removeEventListener("scroll", this.throttledOnScrollListener);
6571

6672
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);
7076

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);
7480
}
7581
}
7682

77-
componentWillReceiveProps (props) {
78-
83+
componentWillReceiveProps(props) {
7984
// do nothing when dataLength is unchanged
8085
if (this.props.dataLength === props.dataLength) return;
8186

@@ -87,25 +92,39 @@ export default class InfiniteScroll extends Component {
8792
});
8893
}
8994

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) {
91110
if (this.state.lastScrollTop) return;
92111

93112
this.dragging = true;
94113
this.startY = evt.pageY || evt.touches[0].pageY;
95114
this.currentY = this.startY;
96115

97-
this._infScroll.style.willChange = 'transform';
116+
this._infScroll.style.willChange = "transform";
98117
this._infScroll.style.transition = `transform 0.2s cubic-bezier(0,0,0.31,1)`;
99118
}
100119

101-
onMove (evt) {
120+
onMove(evt) {
102121
if (!this.dragging) return;
103122
this.currentY = evt.pageY || evt.touches[0].pageY;
104123

105124
// user is scrolling down to up
106125
if (this.currentY < this.startY) return;
107126

108-
if ((this.currentY - this.startY) >= this.props.pullDownToRefreshThreshold) {
127+
if (this.currentY - this.startY >= this.props.pullDownToRefreshThreshold) {
109128
this.setState({
110129
pullToRefreshThresholdBreached: true
111130
});
@@ -114,11 +133,12 @@ export default class InfiniteScroll extends Component {
114133
// so you can drag upto 1.5 times of the maxPullDownDistance
115134
if (this.currentY - this.startY > this.maxPullDownDistance * 1.5) return;
116135

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)`;
119139
}
120140

121-
onEnd (evt) {
141+
onEnd(evt) {
122142
this.startY = 0;
123143
this.currentY = 0;
124144

@@ -129,29 +149,36 @@ export default class InfiniteScroll extends Component {
129149
}
130150

131151
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";
135155
});
136156
}
137157

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;
141163

142-
return (target.scrollTop + clientHeight) >= scrollThreshold * target.scrollHeight;
164+
return (
165+
target.scrollTop + clientHeight >= scrollThreshold * target.scrollHeight
166+
);
143167
}
144168

145-
onScrollListener (event) {
146-
if (typeof this.props.onScroll === 'function') {
169+
onScrollListener(event) {
170+
if (typeof this.props.onScroll === "function") {
147171
// Execute this callback in next tick so that it does not affect the
148172
// functionality of the library.
149173
setTimeout(() => this.props.onScroll(event), 0);
150174
}
151175

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;
155182

156183
// return immediately if the action has already been triggered,
157184
// prevents multiple triggers.
@@ -161,43 +188,49 @@ export default class InfiniteScroll extends Component {
161188

162189
// call the `next` function in the props to trigger the next data fetch
163190
if (atBottom && this.props.hasMore) {
164-
this.setState({actionTriggered: true, showLoader: true});
191+
this.setState({ actionTriggered: true, showLoader: true });
165192
this.props.next();
166193
}
167-
this.setState({lastScrollTop: target.scrollTop});
194+
this.setState({ lastScrollTop: target.scrollTop });
168195
}
169196

170-
render () {
197+
render() {
171198
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",
175202
...this.props.style
176203
};
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);
178207

179208
// because heighted infiniteScroll visualy breaks
180209
// 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+
: {};
183214
return (
184215
<div style={outerDivStyle}>
185216
<div
186-
className='infinite-scroll-component'
187-
ref={infScroll => this._infScroll = infScroll}
217+
className="infinite-scroll-component"
218+
ref={infScroll => (this._infScroll = infScroll)}
188219
style={style}
189220
>
190221
{this.props.pullDownToRefresh && (
191222
<div
192-
style={{ position: 'relative' }}
193-
ref={pullDown => this._pullDown = pullDown}
223+
style={{ position: "relative" }}
224+
ref={pullDown => (this._pullDown = pullDown)}
194225
>
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+
>
201234
{!this.state.pullToRefreshThresholdBreached &&
202235
this.props.pullDownToRefreshContent}
203236
{this.state.pullToRefreshThresholdBreached &&
@@ -206,7 +239,9 @@ export default class InfiniteScroll extends Component {
206239
</div>
207240
)}
208241
{this.props.children}
209-
{!this.state.showLoader && !hasChildren && this.props.hasMore &&
242+
{!this.state.showLoader &&
243+
!hasChildren &&
244+
this.props.hasMore &&
210245
this.props.loader}
211246
{this.state.showLoader && this.props.hasMore && this.props.loader}
212247
{!this.props.hasMore && this.props.endMessage}
@@ -221,7 +256,7 @@ InfiniteScroll.defaultProps = {
221256
releaseToRefreshContent: <h3>Release to refresh</h3>,
222257
pullDownToRefreshThreshold: 100,
223258
disableBrowserPullToRefresh: true
224-
}
259+
};
225260

226261
InfiniteScroll.propTypes = {
227262
next: PropTypes.func,
@@ -240,5 +275,5 @@ InfiniteScroll.propTypes = {
240275
pullDownToRefreshThreshold: PropTypes.number,
241276
refreshFunction: PropTypes.func,
242277
onScroll: PropTypes.func,
243-
dataLength: PropTypes.number.isRequired,
278+
dataLength: PropTypes.number.isRequired
244279
};

0 commit comments

Comments
 (0)