A performant and comprehensive React sticky component.

Related tags

react ui web react-sticky
Overview

react-stickynode

npm version Build Status Coverage Status

A performant and comprehensive React sticky component.

A sticky component wraps a sticky target and keeps the target in the viewport as the user scrolls the page. Most sticky components handle the case where the sticky target is shorter than the viewport, but not the case where a sticky target is taller than the viewport. The reason is that the expected behavior and implementation is much more complicated.

react-stickynode handles not only regular case but the long sticky target case in a natural way. In the regular case, when scrolling the page down, react-stickynode will stick to the top of the viewport. But in the case of a taller sticky target, it will scroll along with the page until its bottom reaches the bottom of the viewport. In other words, it looks like the bottom of viewport pulls the bottom of a sticky target down when scrolling the page down. On the other hand, when scrolling the page up, the top of viewport pulls the top of a sticky target up.

This behavior gives the content in a tall sticky target more chance to be shown. This is especially good for the case where many ADs are in the right rail.

Another highlight is that react-stickynode can handle the case where a sticky target uses percentage as its width unit. For a responsive designed page, it is especially useful.

Features

  • Retrieve scrollTop only once for all sticky components.
  • Listen to throttled scrolling to have better performance.
  • Use rAF to update sticky status to have better performance.
  • Support top offset from the top of screen.
  • Support bottom boundary to stop sticky status.
  • Support any sticky target with various width units.

Usage

The sticky uses Modernizr csstransforms3d and prefixed (link) features to detect IE8/9, so it can downgrade not to use transform3d.

import Sticky from 'react-stickynode';

<Sticky enabled={true} top={50} bottomBoundary={1200}>
    <YourComponent/>
</Sticky>
import Sticky from 'react-stickynode';

<Sticky top='#header' bottomBoundary='#content'>
    <YourComponent/>
</Sticky>

Props

Name Type Note
enabled Boolean The switch to enable or disable Sticky (true by default).
top Number/String The offset from the top of window where the top of the element will be when sticky state is triggered (0 by default). If it is a selector to a target (via querySelector()), the offset will be the height of the target.
bottomBoundary Number/String The offset from the top of document which release state will be triggered when the bottom of the element reaches at. If it is a selector to a target (via querySelector()), the offset will be the bottom of the target.
innerZ Number/String z-index of the sticky.
enableTransforms Boolean Enable the use of CSS3 transforms (true by default).
activeClass String Class name to be applied to the element when the sticky state is active (active by default).
innerClass String Class name to be applied to the inner element ('' by default).
className String Class name to be applied to the element independent of the sticky state.
releasedClass String Class name to be applied to the element when the sticky state is released (released by default).
onStateChange Function Callback for when the sticky state changes. See below.
shouldFreeze Function Callback to indicate when the sticky plugin should freeze position and ignore scroll/resize events. See below.

Handling State Change

You can be notified when the state of the sticky component changes by passing a callback to the onStateChange prop. The callback will receive an object in the format {status: CURRENT_STATUS}, with CURRENT_STATUS being an integer representing the status:

Value Name Note
0 STATUS_ORIGINAL The default status, located at the original position.
1 STATUS_RELEASED The released status, located at somewhere on document, but not default one.
2 STATUS_FIXED The sticky status, located fixed to the top or the bottom of screen.

You can access the statuses as static constants to use for comparison.

import Sticky from 'react-stickynode';

const handleStateChange = (status) => {
    if (status.status === Sticky.STATUS_FIXED) {
        console.log('the component is sticky');
    }
}

<Sticky onStateChange={handleStateChange}>
    <YourComponent/>
</Sticky>

Also Sticky supports children functions:

import Sticky from 'react-stickynode';

<Sticky>
  {status => {
    if (status.status === Sticky.STATUS_FIXED) {
      return 'the component is sticky';
    }
    if (status.status === Sticky.STATUS_ORIGINAL) {
      return 'the component in the original position';
    }
    return 'the component is released'
  }}
</Sticky>

Freezing

You can provide a function in the shouldFreeze prop which will tell the component to temporarily stop updating during prop and state changes, as well as ignore scroll and resize events. This function should return a boolean indicating whether the component should currently be frozen.

Install & Development

Install

npm install react-stickynode

Unit Test

npm run test

Linting

npm run lint

License

This software is free to use under the BSD license. See the LICENSE file for license text and copyright information.

Issues
  • Adding support for shouldFreeze hook

    Adding support for shouldFreeze hook

    Adding the ability to temporarily "turn off" a sticky node. This is intended to be different from the enabled prop, which resets the Sticky element - instead, this is a temporary disabling of the component so it doesn't respond to prop changes and scroll/resize events.

    Also switching the package used to test for prop and state changes, because is-equal-shallow is not suitable for testing props, which includes an object property (children) and thus will always cause isEqual() to return false. I'm open to using different methods for testing for prop changes however, or eliminating this check altogether if the perf impact isn't significant.

    opened by src-code 21
  • Adds onStickyStateChange callback function to props

    Adds onStickyStateChange callback function to props

    This callback will be fired when the sticky state of the component changes, and takes in an isSticky boolean which will be true if the current state is "sticky".

    opened by eligolding 18
  • Breaks building of a create-react-app application

    Breaks building of a create-react-app application

    Updating from 1.4.1 to 2.0.1 breaks the production build of an app created with create-react-app.

    The error lies in the subscribe-ui-event dependency.

    Failed to minify the code from this file:
    
            ./node_modules/subscribe-ui-event/dist-es/lib/listen.js:28
    
    Read more here: http://bit.ly/2tRViJ9
    
    opened by koox00 15
  • componentWillReceiveProps does not check props.enabled

    componentWillReceiveProps does not check props.enabled

    Steps to reproduce

    • Disable Sticky using the prop enabled={false}
    • Set up a component to receive other props (either by a re-render by parent) or dynamically changing some other prop on Sticky
    • Expectation: Methods like updateInitialDimension & update are not called due to the component being NOT enabled
    • Actual: When disabled, errors occur due to attempts to find the DOM node (or other issues) as the correct setup has not occurred.

    Receiving Error

    • Sticky.js:128 Uncaught TypeError: Cannot read property 'value' of null at Sticky.getBottomBoundary (Sticky.js:128) which maps to here https://github.com/yahoo/react-stickynode/blob/e4668b50226b8ebc78fe6ee4ff80d3423497a633/src/Sticky.jsx#L285
        componentWillReceiveProps (nextProps) {
            this.updateInitialDimension(nextProps);
            this.update();
        }
    

    Potential Fix

    • componentWillReceiveProps should check this.props.enabled before continuing and not call this.updateInitialDimension(nextProps); this.update(); if enabled is false
    • Similar to the checks in componentDidUpdate and componentDidMount
    opened by lb- 14
  • Refactor legacy code - use PureComponent instead of shallowCompare

    Refactor legacy code - use PureComponent instead of shallowCompare

    Easy refactoring and remove dependency.

    https://www.npmjs.com/package/react-addons-shallow-compare

    "react-addons-shallow-compare Note: This is a legacy React addon, and is no longer maintained.

    We don't encourage using it in new code, but it exists for backwards compatibility. The recommended migration path is to use React.PureComponent instead."

    opened by kunukn 14
  • support react16

    support react16

    this pr resolves https://github.com/yahoo/react-stickynode/issues/83

    opened by brucewpaul 12
  • Fork/rename this project

    Fork/rename this project

    Currently there's no one within Yahoo/Oath that has a desire to continue maintaining this project, so it'd be great if someone were to step forward and fork/rename this project and maintain it going forward so we can archive this repo.

    Anyone willing?

    cc @hankhsiao @roderickhsiao @markwoon @kunukn @aclimatt @redonkulus

    help wanted 
    opened by src-code 10
  • Sticky should sticky to another sticky

    Sticky should sticky to another sticky

    https://github.com/yahoo/react-stickynode/blob/master/src/Sticky.jsx#L292-L294

    If the top property refers to another Sticky instance, it should consider the offset of that Sticky instance together with its height.

    @hankhsiao

    opened by 100kV 10
  • Remove use of self instead of this

    Remove use of self instead of this

    Assigning var self = this; through the Sticky.jsx file served no purpose (and as self is now used in browsers, it shadowed a global variable)

    opened by codeheroics 10
  • I want to apply sticky to vertical and horizontal scrolling.

    I want to apply sticky to vertical and horizontal scrolling.

    For example, with code like sample code, in a section such as a table, you can fix the header row when scrolling horizontally. Then, when you scroll vertically, can you make the header row follow (sticky) ?

    Of course, I can use js to get the value when the scroll position changes and control the header using transform, ect., but I don't want to handle self-code if possible. thanks.

    If there is no such feature in this library, I will try to change the UI in js, but if there is, I would appreciate it if you could let me know.

    opened by ogasawaraShinnosuke 2
  • Can not import library in codepen

    Can not import library in codepen

    Hi everyone,

    I would like to create an issue with a codepen but as soon as I add import to "react-stickynode" in a codepen, it broke the compilation. Is there any workaround to get "Sticky" working in a codepen ?

    Ex : https://codesandbox.io/s/adoring-meninsky-jz2jz?file=/src/App.js

    Yours faithfully, LCDP

    opened by LeComptoirDesPharmacies 2
  • Sticky only when custom target is out of viewport

    Sticky only when custom target is out of viewport

    Hi everyone,

    Context : We have a header on two rows : First => Menu Second => Search bar

    "Search bar" (Second row) should always be sticky but "Menu" (First row) should only be sticky if user scroll back some pixels. We successfully hide/display the "Menu" (First row) according to scroll back status.

    However, we would like the full header to become sticky only when "Search bar" (Second row) is out of viewport. At the moment, the header become sticky when any children of the component is out of viewport.

    Question : Is there any way to say "Sticky" which component it should listen position on ?

    For example :

    <Sticky target={customRef}>
         <ThisContentIsOptional />
         <ThisContentShouldSticky ref={customRef} />
    </Sticky>
    

    Note : We tried to nest Sticky components in order to listen "STATUS" event of children and enable/disable parent Sticky but as soon as "Sticky" is not enabled, "onStateChange" is never called.

    Ex :

    <Sticky enabled={childrenSticky}>
         <ThisContentIsOptional />
         <Sticky 
             enabled={false}
             onStateChange={status => {
                    if (status.status === Sticky.STATUS_FIXED) {
                      setChildrenSticky(true);
                    } else {
                      setChildrenSticky(false);
                    }
                  }}
         >
           <ThisContentShouldSticky />
         </Sticky>
    </Sticky>
    

    Thansk in advance for your help. Yours faithfully, LCDP

    opened by LeComptoirDesPharmacies 0
  • Scroll on container other than body but still full page/window/viewport

    Scroll on container other than body but still full page/window/viewport

    According to issue #26 react-stickynode does not work if scrollable container is not <body />.

    hankhsiao commented on Jul 6, 2016:

    Sticky is not designed in this case. It needs fundamental changes. There are some fundamental differences. For example, to handle sticking to the viewport with high performance, we use position: fixed instead of using position: absolute and changing top all the time when scrolling (I know some sticky components do that). In this case, fixed won't be correct in a scrollable DIV.

    I my app using NextJS which adds a div below body that cannot be removed (see https://stackoverflow.com/questions/65374790/can-you-modify-nextjs-mount-element-or-add-classes-to-next-div)>

    I am still scrolling the full page / viewport though, so there would be no extra logic to add. Is there a workaround for this ?

    opened by didrip 0
  • On faster scroll, Sticky overflows other items

    On faster scroll, Sticky overflows other items

    I have a weird issue, maybe it is just my machine but I haven't seen this issue when adding sticky directly through css.

    I have a sticky card which appears between 2 elements, it works when I slow scroll, but when I scroll faster (and by fast I mean just quicker than slow, not majorly fast) the Sticky container overshoots the top and bottom, then resets, almost like it takes a second to remove the sticky class.

    Maybe I am missing a delay setting or something? Has anyone else experienced this. Its not a particularly resource intensive page that it should be that slow.

    opened by willslater 2
  • onStateChange is never called

    onStateChange is never called

    Don't know if I use the library in the correct way.

    I have an < Element / > component which is first visible after scrolling down a little bit. It sits together with some other elements, which can vary in their amount, depending how many the user adds. The more the user adds, the more is < Element / > pushed to the bottom.

    As soon as it hits the bottom of the viewport I want it to be sticky to the bottom.

    As for now the onStateChange is never called.

    I think I might just miss how to use this library properly :D

    <Sticky
        bottomBoundary={window.innerHeight - 70}
        enabled
        onStateChange={(status) => {
          console.log(status);
        }}
        top={50}
    >
        <Element />
    </Sticky>
    
    opened by lehnavid 1
  • Bottomboundary not triggered with changesd props, its consider only the first value passed to it.

    Bottomboundary not triggered with changesd props, its consider only the first value passed to it.

    <Sticky enabled={true} top={59} bottomBoundary={bottomBoundary}>

    Here I'm passing two passing different classes to bottomBoundary variable based on condition But it considers the first props not the later on.

    I wanted to bottomBoundary to be dynamic based on props passed.

    opened by sandeepdhobi 1
  • Safari having bug when scroll to top

    Safari having bug when scroll to top

    I met a bug is when in Safari, when I tap to scroll to top, it will leave a huge blank space

    screen shot 2018-06-20 at 2 29 30 pm

    After I investigate is due to the STATUS_FIXED is not respond correctly. At Sticky.jsx, line365, the position value should change base on STATUS_FIXED value, but it somehow not changing in Safari when I scrolling fast to top, I try to add another handling window.pageYOffset for it, it fixed the bug. position: this.state.status === STATUS_FIXED && window.pageYOffset ? 'fixed' : 'relative', I hope you guys can update it or maybe find another way to fix it, thanks.

    opened by youwei91 4
Releases(v4.0.0)
  • v4.0.0(Aug 11, 2021)

    Feature

    • feat: publish es modules (#554) 9634ffa

    Development

    • test: fix functional test flow (#543) 34efcc4

    Dependencies

    • chore(deps-dev): bump webpack from 5.49.0 to 5.50.0 (#552) 83d9174
    • chore(deps-dev): bump @babel/register from 7.14.5 to 7.15.3 (#553) bff5432
    • chore(deps-dev): bump @wdio/cli from 7.9.0 to 7.9.1 (#549) 302bde1
    • chore(deps-dev): bump @wdio/sauce-service from 7.9.0 to 7.9.1 (#547) ddeae6f
    • chore(deps): bump core-js from 3.16.0 to 3.16.1 (#548) 2292bd0
    • chore(deps-dev): bump @wdio/local-runner from 7.9.0 to 7.9.1 (#546) cbdc879
    • chore(deps-dev): bump @wdio/mocha-framework from 7.9.0 to 7.9.1 (#545) 9e4f0ae
    • chore(deps-dev): bump webpack from 5.48.0 to 5.49.0 (#544) 807740d
    • chore(deps-dev): bump @babel/preset-env from 7.14.9 to 7.15.0 (#542) b9f3376
    • chore(deps-dev): bump @babel/core from 7.14.8 to 7.15.0 (#541) a59b65d
    • chore: update package-lock.json format 43dc4f4

    https://github.com/yahoo/react-stickynode/compare/v3.1.1...v4.0.0

    Source code(tar.gz)
    Source code(zip)
  • v3.1.1(Aug 4, 2021)

  • v3.0.5(Mar 13, 2021)

  • v3.0.4(Oct 12, 2020)

  • v3.0.3(Jun 5, 2020)

  • v3.0.1(May 5, 2020)

  • v3.0.0(May 5, 2020)

  • v2.1.0(Oct 30, 2018)

  • v2.0.1(May 29, 2018)

  • v2.0.0(May 29, 2018)

    This release update the refs usage to the new api: https://reactjs.org/docs/refs-and-the-dom.html

    "Legacy API: String Refs If you worked with React before, you might be familiar with an older API where the ref attribute is a string, like "textInput", and the DOM node is accessed as this.refs.textInput. We advise against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. If you’re currently using this.refs.textInput to access refs, we recommend the callback pattern instead."

    Thanks to @kunukn for contributing!

    Source code(tar.gz)
    Source code(zip)
  • v1.4.1(Jan 22, 2018)

  • v1.2.0(Aug 5, 2016)

  • v1.1.5(Jul 18, 2016)

    Switching to use react-addons-shallow-compare instead of is-equal-shallow, which tends to return false negatives for props. This should result in fewer unnecessary re-renders of the stickynode component, and so better performance.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.4(Jul 15, 2016)

  • v1.1.3(Jun 17, 2016)

  • v1.1.2(Jun 2, 2016)

    If a scrollStart event is received but the scrollTop value doesn't seem to have changed from the cached value, skip handling the event and the subsequent scroll event. This fixes an issue seen when the document's scroll position is being restored (eg, during a page navigation event in a single page app) and sticky may incorrectly attempt to reposition itself due to the scroll event.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(May 31, 2016)

  • v1.1.1(May 31, 2016)

  • v1.0.24(May 4, 2016)

  • v1.0.23(May 2, 2016)

  • v1.0.22(May 2, 2016)

  • v1.0.21(Apr 20, 2016)

  • v1.0.20(Apr 20, 2016)

  • v1.0.19(Apr 12, 2016)

  • v1.0.18(Apr 6, 2016)

    In many situations, we need to do something according to sticky status. Previously, we had contextual class to help us know sticky status, now we have a callback function! Thank you @eligolding.

    https://github.com/yahoo/react-stickynode#handling-state-change

    Source code(tar.gz)
    Source code(zip)
Owner
Yahoo
Yahoo is a Verizon Media brand. This organization is the home to many of the active open source projects published by engineers at Yahoo and Verizon Media.
Yahoo
A performant and comprehensive React sticky component.

react-stickynode A performant and comprehensive React sticky component. A sticky component wraps a sticky target and keeps the target in the viewport

Yahoo 945 Oct 14, 2021
Hide your header until you need it

react-headroom Hide your header until you need it. Demo http://kyleamathews.github.io/react-headroom Install npm install react-headroom Usage A simple

Kyle Mathews 1.6k Oct 10, 2021