UNPKG

@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
# react-scroll-percentage [![Greenkeeper badge](https://badges.greenkeeper.io/thebuilder/react-scroll-percentage.svg)](https://greenkeeper.io/) [![Travis](https://travis-ci.org/thebuilder/react-scroll-percentage.svg?branch=master)](https://travis-ci.org/thebuilder/react-scroll-percentage) [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![npm](https://img.shields.io/npm/v/react-scroll-percentage.svg)](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> ``` ## Demo See https://thebuilder.github.io/react-scroll-percentage/ for a demo. ## Installation Install using [Yarn](https://yarnpkg.com): ```sh yarn add react-scroll-percentage ``` or NPM: ```sh npm install react-scroll-percentage --save ``` ## Props 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 | ## Example code ### Render prop 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> ``` ### OnChange callback 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> ``` ## Polyfills ### Intersection Observer [Intersection Observer](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 [polyfill](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 ) } ``` ### requestAnimationFrame To optimize scroll updates, [requestAnimationFrame](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.