├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── src └── component.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nicholas Rakoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Scroll Load Component 2 | ================================= 3 | 4 | A dummy React component to implement continuous load on scroll for **modern browser**. 5 | 6 | ### Usage 7 | ``` 8 | var ScrollLoad = require('react-component-scrollload'); 9 | ``` 10 | ```html 11 | }> 12 | {items} 13 | 14 | ``` 15 | 16 | `npm install react-component-scroll --save` 17 | 18 | ### Prop types 19 | ```javascript 20 | propTypes: { 21 | hasMore: React.PropTypes.bool.isRequired, // if there is more to load 22 | loadMore: React.PropTypes.func.isRequired, // callback to load more 23 | isLoading: React.PropTypes.bool.isRequired, // indicate if a loading is ongoing 24 | useDocument: React.PropTypes.bool, // if true, the scrolling is calculated based on the document and not the element, default false 25 | threshold: React.PropTypes.number, // pixel threshold, default 1000 26 | loader: React.PropTypes.component, // displayed loader component, default React.DOM.div(null, 'Loading...') 27 | 28 | // disable pointer to improve scrolling perf 29 | disablePointer: React.PropTypes.number, // ms delay until disablePointerClass class is removed after last scroll event, default 0 (feature disabled) 30 | disablePointerClass: React.PropTypes.string // default class added to child wrapper div, default 'disable-pointer' 31 | } 32 | ``` 33 | 34 | #### Scroll performance 35 | 36 | A dummy way to improve scrolling performance. 37 | 38 | Add to your css a `disable-pointer` definition, default name defined by `disablePointerClass`. 39 | 40 | ```css 41 | .disable-pointer { 42 | pointer-events: none !important; 43 | } 44 | ``` 45 | 46 | Add `disablePointer` prop with a delay in milliseconds. 47 | ```html 48 | ... 49 | ``` 50 | 51 | 52 | ### Licence 53 | 54 | MIT 55 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/component'); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-component-scrollload", 3 | "version": "0.5.1", 4 | "description": "React component to implement continuous load on scroll", 5 | "main": "index.js", 6 | "keywords": ["react", "infinite", "continuous", "scroll", "react-component"], 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/nrako/react-component-scrollload.git" 13 | }, 14 | "author": "@nrako", 15 | "license": "MIT", 16 | "dependencies": { 17 | "document-offset": "^1.0.4" 18 | }, 19 | "devDependencies": { 20 | "react": "^0.14.0", 21 | "react-dom": "^0.14.0" 22 | }, 23 | "peerDependencies": { 24 | "react": ">= 0.14.0", 25 | "react-dom": ">= 0.14.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/component.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | var documentOffset = require('document-offset'); 4 | 5 | var ContinuousScroll = React.createClass({ 6 | propTypes: { 7 | hasMore: React.PropTypes.bool.isRequired, 8 | loadMore: React.PropTypes.func.isRequired, 9 | isLoading: React.PropTypes.bool.isRequired, 10 | useDocument: React.PropTypes.bool, 11 | threshold: React.PropTypes.number, 12 | loader: React.PropTypes.element, 13 | disablePointer: React.PropTypes.number, 14 | disablePointerClass: React.PropTypes.string 15 | }, 16 | getDefaultProps: function () { 17 | return { 18 | threshold: 1000, 19 | loader: React.createElement('div', null, 'Loading...'), 20 | disablePointer: 0, 21 | disablePointerClass: 'disable-pointer' 22 | }; 23 | }, 24 | disablePointerTimeout: null, 25 | onScroll: function () { 26 | if (!this.isMounted()) 27 | return; 28 | 29 | if (this.props.disablePointer > 0) 30 | this.disablePointer(); 31 | 32 | if (!this.props.hasMore || this.props.isLoading) 33 | return; 34 | 35 | var el = ReactDOM.findDOMNode(this); 36 | var currentScroll; 37 | 38 | if (this.props.useDocument) { 39 | // See http://stackoverflow.com/a/28633515/564163 40 | currentScroll = ( 41 | window.pageYOffset || 42 | document.documentElement.scrollTop || 43 | document.body.scrollTop || 44 | 0 45 | ) + documentOffset(el).top; 46 | } else { 47 | currentScroll = el.scrollTop + el.offsetHeight; 48 | } 49 | 50 | if(currentScroll + this.props.threshold < el.scrollHeight) 51 | return; 52 | 53 | this.props.loadMore(); 54 | }, 55 | disablePointer: function () { 56 | if (this.disablePointerTimeout === null) 57 | this.refs.wrapper.classList.add(this.props.disablePointerClass); 58 | 59 | clearTimeout(this.disablePointerTimeout); 60 | this.disablePointerTimeout = setTimeout(this.removeDisablePointerClass, this.props.disablePointer); 61 | }, 62 | removeDisablePointerClass: function () { 63 | if (this.refs.wrapper) 64 | this.refs.wrapper.classList.remove(this.props.disablePointerClass); 65 | 66 | this.disablePointerTimeout = null; 67 | }, 68 | componentDidMount: function () { 69 | this.listenScroll(); 70 | 71 | // About setTimeout: fluxxor enforce flux principle; dispatching an action during and action would trigger an error 72 | setTimeout(this.onScroll); 73 | }, 74 | componentDidUpdate: function () { 75 | // when component update need to check if loaded children height are bigger than threshold else load more 76 | // About setTimeout: fluxxor enforce flux principle; dispatching an action during and action would trigger an error 77 | setTimeout(this.onScroll); 78 | }, 79 | componentWillUnmount: function () { 80 | this.unlistenScroll(); 81 | }, 82 | listenScroll: function () { 83 | var el = this.props.useDocument ? window : ReactDOM.findDOMNode(this); 84 | 85 | el.addEventListener('scroll', this.onScroll); 86 | window.addEventListener('resize', this.onScroll); 87 | }, 88 | unlistenScroll: function () { 89 | var el = this.props.useDocument ? window : ReactDOM.findDOMNode(this); 90 | 91 | el.removeEventListener('scroll', this.onScroll); 92 | window.removeEventListener('resize', this.onScroll); 93 | }, 94 | componentWillReceiveProps: function (nextProps) { 95 | // if there is no need to listen on scroll anymore 96 | if (nextProps.hasMore) 97 | this.listenScroll(); 98 | else if (this.props.disablePointer <= 0) 99 | this.unlistenScroll(); 100 | }, 101 | render: function() { 102 | return ( 103 | React.createElement('div', this.props, 104 | React.createElement('div', {ref: 'wrapper'}, 105 | this.props.children 106 | ), 107 | this.props.isLoading && this.props.loader 108 | ) 109 | ); 110 | } 111 | 112 | }); 113 | 114 | module.exports = ContinuousScroll; 115 | --------------------------------------------------------------------------------