React-custom-scrollbars: Prevent nested scrollbar from scrolling document

Created on 24 Oct 2015  路  5Comments  路  Source: malte-wessel/react-custom-scrollbars

First, thank you very much for writing this component. I've tried many different custom scrollbar components but this is the first one that I literally just dropped into my code and it worked flawlessly. Plus the default styling happens to be exactly what I was going for. Saved me tons of time and is definitely appreciated.

One thing that I'm trying to do is prevent scrolling on a div from causing the entire document to scroll when you get to the top or bottom of the div. I thought I could cancel the scroll event from the onScroll handler but it doesn't seem to work. I looked at the code and it's not clear to me why it wouldn't.

Here's my code:

  handleScroll(e, values) {
    if (values.top === 0 || values.top === 1) {
      e.stopImmediatePropagation();
      e.preventDefault();
      e.returnValue = false;
      return false;
    }
  }

  render() {
    let { children } = this.props;
    let scrollerStyle = {height: 400px, width: 'auto'};

    return (
      <Scrollbars
        style={scrollerStyle}
        onScroll={this.handleScroll.bind(this)}>
        {children}
      </Scrollbars>
    );
  }

Most helpful comment

the above code is not working, this code will work

  handleScroll(e, values) {
   var scrollarea = this.refs.container.parentNode;
      var dir =  (e.detail<0) ? 1 : (e.wheelDelta>0) ? 1 : -1;
      if ((dir == 1 && scrollarea.scrollTop == 0) || (dir == -1 && scrollarea.scrollTop == scrollarea.scrollHeight-scrollarea.clientHeight)){
        e.preventDefault();
      }
  }

  componentDidMount() {
    this.refs.container.parentNode.addEventListener('scroll', this.handleScroll);
    this.refs.container.parentNode.addEventListener('wheel', this.handleScroll);
  }

  componentWillUnmount(){
    this.refs.container.parentNode.removeEventListener('scroll', this.handleScroll);
    this.refs.container.parentNode.removeEventListener('wheel', this.handleScroll);
  }
  render() {
    return (
         <Scrollbars>
             <div ref="container">
             </div>
         </Scrollbars>
    );
  }

All 5 comments

Hey bunkat, the component uses the native browser scrolling (the native scrollbars are just hidden). I think the described behaviour is the browser's default. If I got you right, you don't want the document to scroll when the mouse is over your component and it has reached it's own top (or bottom).

I would have thought that this would be possible with event.stopPropagation() but maybe you could get it to work like this:

  • store your component's state (top value), so that you can access it from anywhere
  • register a scroll event listener on the body (or document)
  • when a scroll event happens, check if your component top value is 0, if so preventDefault.
class MyScrollbars extends Component {
  handleScroll(e, values) {
    this.setState(values);
  }

  handleDocumentScroll(e) {
    const { top } = this.state;
    if (top <= 1) e.preventDefault()
  }

  componentDidMount() {
    document.addEventListener('scroll', this.handleDocumentScroll.bind(null, this);
  }

  render() {
    return (
      <Scrollbars
        onScroll={this.handleScroll.bind(null, this)}>
        {this.props.children}
      </Scrollbars>
    );
  }
}

(this.setState could have cause multiple renders; if you want to, you can store the value directy on the component with this._top = values.top and then in handleDocumentScroll if (this._top <= 1))

the above code is not working, this code will work

  handleScroll(e, values) {
   var scrollarea = this.refs.container.parentNode;
      var dir =  (e.detail<0) ? 1 : (e.wheelDelta>0) ? 1 : -1;
      if ((dir == 1 && scrollarea.scrollTop == 0) || (dir == -1 && scrollarea.scrollTop == scrollarea.scrollHeight-scrollarea.clientHeight)){
        e.preventDefault();
      }
  }

  componentDidMount() {
    this.refs.container.parentNode.addEventListener('scroll', this.handleScroll);
    this.refs.container.parentNode.addEventListener('wheel', this.handleScroll);
  }

  componentWillUnmount(){
    this.refs.container.parentNode.removeEventListener('scroll', this.handleScroll);
    this.refs.container.parentNode.removeEventListener('wheel', this.handleScroll);
  }
  render() {
    return (
         <Scrollbars>
             <div ref="container">
             </div>
         </Scrollbars>
    );
  }

Thx @mrwnmonm. I'll close this then.

@mrwnmonm it's method doesn't work on mobile( why?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

umer4ik picture umer4ik  路  4Comments

webchaz picture webchaz  路  3Comments

ilfa picture ilfa  路  5Comments

leofle picture leofle  路  4Comments

luxotus picture luxotus  路  4Comments