@kohlmannj/react-scroll-percentage
Version:
Monitor the scroll percentage of a component inside the viewport, using the IntersectionObserver API.
148 lines (107 loc) • 5.3 kB
Markdown
[](https://greenkeeper.io/)
[](https://travis-ci.org/thebuilder/react-scroll-percentage)
[](https://github.com/prettier/prettier)
[](https://www.npmjs.com/package/react-scroll-percentage)
React component that reports the current scroll percentage of a element inside
the viewport. It uses [React Intersection
Observer](https://github.com/thebuilder/react-intersection-observer) to only
report the percentage when the element is inside the viewport.
```js
import ScrollPercentage from 'react-scroll-percentage'
<ScrollPercentage>
{( percentage ) => (
<h2>{`Percentage scrolled: ${percentage.toPrecision(2)}%.`}</h2>
)}
</ScrollPercentage>
```
See https://thebuilder.github.io/react-scroll-percentage/ for a demo.
Install using [Yarn](https://yarnpkg.com):
```sh
yarn add react-scroll-percentage
```
or NPM:
```sh
npm install react-scroll-percentage --save
```
The **`<ScrollPercentage />`** accepts the following props:
| Name | Type | Default | Required | Description |
| --------- | ----------------------------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------- |
| tag | Node | 'div' | true | Element tag to use for the wrapping component |
| children | ((percentage: number, inView: boolean) => Node) | | true | Children should be either a function or a node |
| threshold | Number | 0 | false | Number between 0 and 1 indicating the percentage that should be visible before triggering |
| onChange | (percentage: number, inView: boolean) => void | | false | Call this function whenever the in view state changes |
| innerRef | (element: ?HTMLElement) => void | | false | Get a reference to the inner DOM node |
The basic usage pass a function as the child. It will be called whenever the
state changes, with the current value of `percentage` and `inView`.
> Note that <ScrollPercentage> will still render a wrapping element (default is a `<div>`).
> You can change to element by setting `tag`, and any excess props like `className` will be passed to the element
```js
import ScrollPercentage from 'react-scroll-percentage'
<ScrollPercentage>
{(percentage, inView ) => (
<h2>{`Percentage scrolled: ${percentage.toPrecision(2)}%.`}</h2>
)}
</ScrollPercentage>
```
You can monitor the onChange method, and control the state in your own
component. The child node will always be rendered.
```js
import ScrollPercentage from 'react-scroll-percentage'
<ScrollPercentage onChange={(percentage, inView) => console.log(percentage, inView)}>
<h2>
Plain children are always rendered. Use onChange to monitor state.
</h2>
</ScrollPercentage>
```
[](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
is the API is used to determine if an element is inside the viewport or not. Browser support is pretty good, but Safari is still missing support.
> [Can i use intersectionobserver?](https://caniuse.com/#feat=intersectionobserver)
You can import the
[](https://www.npmjs.com/package/react-intersection-observer) directly or use
a service like [polyfill.io](https://polyfill.io/v2/docs/) to add it when
needed.
```sh
yarn add intersection-observer
```
Then import it in your app:
```js
import 'intersection-observer'
```
If you are using Webpack (or similar) you could use [dynamic
imports](https://webpack.js.org/api/module-methods/#import-), to load the
Polyfill only if needed. A basic implementation could look something like this:
```js
loadPolyfills()
.then(() => /* Render React application now that your Polyfills are ready */)
/**
* Do feature detection, to figure out which polyfills needs to be imported.
**/
function loadPolyfills() {
const polyfills = []
if (!supportsIntersectionObserver()) {
polyfills.push(import('intersection-observer'))
}
return Promise.all(polyfills)
}
function supportsIntersectionObserver() {
return (
'IntersectionObserver' in global &&
'IntersectionObserverEntry' in global &&
'intersectionRatio' in IntersectionObserverEntry.prototype
)
}
```
To optimize scroll updates,
[](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
is used. Make sure your target browsers support it, or include the required
polyfill.