Skip to content

Instantly share code, notes, and snippets.

@tmocellin
Created August 3, 2015 14:05
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save tmocellin/0c8ffb3d54df8a1c8966 to your computer and use it in GitHub Desktop.
Save tmocellin/0c8ffb3d54df8a1c8966 to your computer and use it in GitHub Desktop.
Handle the scroll event in react js and change the navbar class when special section is on top of windows
var Hello = React.createClass({
getOffset : function(element){
var bounding = element.getBoundingClientRect();
return {
top: bounding.top + document.body.scrollTop,
left: bounding.left + document.body.scrollLeft
};
},
handleScroll : function(){
navbar = React.findDOMNode(this.refs.navbar);
startElement = React.findDOMNode(this.refs.sec2);
offset = this.getOffset(startElement)
windowsScrollTop = window.pageYOffset;
if(windowsScrollTop >= offset.top){
navbar.classList.add("navbar-fixed-top");
}else{
navbar.classList.remove("navbar-fixed-top");
}
},
componentDidMount: function() {
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount: function() {
window.removeEventListener('scroll', this.handleScroll);
},
render: function() {
return <div>
<div className="navbar navbar-default" ref="navbar" id="navbar" role="navigation">
<div className="container">
<div className="navbar-header">
<a className="navbar-brand" href="#">Project name</a>
</div>
</div>
</div>
<section id="sec1"></section>
<section id="sec2" ref="sec2"></section>
<section id="sec3"></section>
</div>;
}
});
React.render(<Hello/>, document.getElementById('container'));
@agm1984
Copy link

agm1984 commented Feb 23, 2018

I also just made one. I created a Gist for it because it took me some time to figure out:

https://gist.github.com/agm1984/092ce379180e94bd13301bb33dbcd29f

Mine checks window.scrollY (debounced to every 16ms) and if the number is ever greater than 0,
it applies different CSS.

import React, { Component } from 'react'
import UserDetails from './UserDetails'

/**
 * This utility function allows function calls to be debounced.
 * @param {Function} func Function that requires debouncing
 * @param {Number} wait Wait time in milliseconds between successive invocations
 */
const debounce = (func, wait) => {
  let timeout
  return (...args) => {
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(this, args), wait)
  }
}

class NavContainer extends Component {
  constructor(props) {
    super(props)
    this.state = {
      scrollPositionY: 0,
    }
  }

  componentDidMount() {
    // 32 is the number of milliseconds to debounce
    // I picked this because it's approx 1 frame (ie: 16.7ms)
    // You'll want to modulate that to your taste.
    // Add console.logs in handleScroll function to check if its flooding.
    return window.addEventListener('scroll', debounce(this.handleScroll, 16))
  }

  componentWillUnmount() {
    return window.removeEventListener('scroll', debounce(this.handleScroll, 16))
  }

  handleScroll = () => {
    // + is unary operator, same as Number(window.scrollY)
    const scrollPositionY = +window.scrollY
    return this.setState({ scrollPositionY })
  }

  render() {
    // !! coerces value to be a Boolean
    // we want it to be true or false (true if scrollPositionY> 0)
    // it works because scrollPositionY=== 0 is falsy
    const isScrolling = !!this.state.scrollPositionY
    return (
      <div className={(isScrolling) ? 'nav isScrolling' : 'nav'}>
        <UserDetails isScrolling={isScrolling} />
      </div>
    )
  }
}

NOTE: I encountered issues destructuring scrollY off window. That's why it's not.

@AbdelrhmanAmin
Copy link

^ Thanks a lot dude it worked.

@OmkarKirpan
Copy link

Thanks a lot dude

@marinelligiovanna
Copy link

Awesome! Thanks

@LoicHa
Copy link

LoicHa commented Nov 28, 2019

I also just made one. I created a Gist for it because it took me some time to figure out:

https://gist.github.com/agm1984/092ce379180e94bd13301bb33dbcd29f

Mine checks window.scrollY (debounced to every 16ms) and if the number is ever greater than 0,
it applies different CSS.

import React, { Component } from 'react'
import UserDetails from './UserDetails'

/**
 * This utility function allows function calls to be debounced.
 * @param {Function} func Function that requires debouncing
 * @param {Number} wait Wait time in milliseconds between successive invocations
 */
const debounce = (func, wait) => {
  let timeout
  return (...args) => {
    clearTimeout(timeout)
    timeout = setTimeout(() => func.apply(this, args), wait)
  }
}

class NavContainer extends Component {
  constructor(props) {
    super(props)
    this.state = {
      scrollPositionY: 0,
    }
  }

  componentDidMount() {
    // 32 is the number of milliseconds to debounce
    // I picked this because it's approx 1 frame (ie: 16.7ms)
    // You'll want to modulate that to your taste.
    // Add console.logs in handleScroll function to check if its flooding.
    return window.addEventListener('scroll', debounce(this.handleScroll, 16))
  }

  componentWillUnmount() {
    return window.removeEventListener('scroll', debounce(this.handleScroll, 16))
  }

  handleScroll = () => {
    // + is unary operator, same as Number(window.scrollY)
    const scrollPositionY = +window.scrollY
    return this.setState({ scrollPositionY })
  }

  render() {
    // !! coerces value to be a Boolean
    // we want it to be true or false (true if scrollPositionY> 0)
    // it works because scrollPositionY=== 0 is falsy
    const isScrolling = !!this.state.scrollPositionY
    return (
      <div className={(isScrolling) ? 'nav isScrolling' : 'nav'}>
        <UserDetails isScrolling={isScrolling} />
      </div>
    )
  }
}

NOTE: I encountered issues destructuring scrollY off window. That's why it's not.

If you make any state update while scrolling you will face choppy scroll performance issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment