React-custom-scrollbars: Prevent page scrolling ?

Created on 9 May 2016  路  7Comments  路  Source: malte-wessel/react-custom-scrollbars

question

Most helpful comment

Since the "how to achieve this" is not really obvious by reading this thread, I'll report my fully working solution.

That said, I reckon this should probably be an optional property of the component: windowScroll={false} to make it work out-of-the-box (despite not working on mobile, as long as it is documented).

class MyComponent extends React.Component {
  componentDidMount() {
    this.refs.wrapper.addEventListener('wheel', e => this.handleWindowWheel(e), false);
  }

  componentWillUnmount() {
    this.refs.wrapper.removeEventListener('wheel', e => this.handleWindowWheel(e));
  }

  handleWindowWheel(e) {
    const { scrollHeight, clientHeight, scrollTop } = this.refs.scrollbars.getValues();
    // When the bottom is reached and we're scrolling down, prevent scrolling of the window
    if (e.deltaY > 0 && (scrollHeight - clientHeight <= scrollTop + 1)) e.preventDefault();
  }

  render() {
    return (
      <div>
        <Scrollbars ref='scrollbars'>
          <div ref='wrapper'>
          </div>
        </Scrollbars>
      </div>
    );
  }
}

I need the <div ref='wrapper'> because this.refs.scrollbars doesn't exist during mount, but if it is part of the code, it should be worn by the actual <Scrollbars /> component wrapper.

Sidenote:

If you get the following warning:

[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952

Unless I'm wrong, this is to be ignored, because this means one will never user event.preventDefault() which is not our case here. :smile:

All 7 comments

I'm not sure if @nhatkhoa here is having the same problem as I do. But I'm trying to indeed prevent the page from scrolling when the bottom of the scrollbar is reached.

Currently I'm fixing this like this:

I put a ref on the scrollbars:

<Scrollbars ref="scrollBars">
    {list}
</Scrollbars>

And I add a custom 'mousewheel' listener to the view ref of the scrollBars.

// simplified, not cross browser proof..
this.refs.scrollBars.refs.view.addEventListener('mousewheel', (evt) => {
  const { scrollHeight, clientHeight, scrollTop } = this.refs.scrollBars.getValues();

  // Prevent if we're at the end
  if (evt.deltaY > 0 && (scrollHeight - clientHeight <= scrollTop)) {
    evt.preventDefault();
  }
}, false);

But this is far from ideal..

Did somebody encountered this problem before / like to share their thoughts on my solution?

Hey, this might work:

var PreventPageScrollingScrollbars = createClass({
  componentDidMount() {
    window.addEventListener("mousewheel", this.handleWindowWheel, false);
  },
  componentWillUnmount() {
    window.removeEventListener("mousewheel", this.handleWindowWheel);
  },
  handleWindowWheel(event) {
    const { top } = this.refs.scrollbars.getValues();
    // When the bottom is reached and we're scrolling down, prevent scrolling of the window
    if (top >= 1 && event.deltaY > 0) event.preventDefault();
  },
  render() {
    return (
      <Scrollbars ref="scrollbars" {...this.props}/>
    );
  }
});

You can then use this component:

<PreventPageScrollingScrollbars>
  {/* Your content */}
</PreventPageScrollingScrollbars>

Yeah that is kindof what I did right? Except I did not wrap it in a component. Good idea, thanks :)

FYI top >= 1 && event.deltaY > 0 does not work all the time. It seems like top is never 1 in my implementation but always something like 0.992 of 0.9997. Checking on top >= 0.99 or adding the delta to the current position does the trick: (scrollHeight - clientHeight <= scrollTop + event.deltaY) && evt.deltaY > 0.

I'm guessing this has something to with my retina screen because it works fine on my extended monitor. Took me a while to figure out that there was actually a difference when I dragged my window to my other screen... I can imagine this might also cause other problems btw.

On using @malte-wessel method , I'am getting this error:

Uncaught TypeError: Cannot read property 'scrollbars' of undefined

I'll close this for now. Please reopen if still not working...

Since the "how to achieve this" is not really obvious by reading this thread, I'll report my fully working solution.

That said, I reckon this should probably be an optional property of the component: windowScroll={false} to make it work out-of-the-box (despite not working on mobile, as long as it is documented).

class MyComponent extends React.Component {
  componentDidMount() {
    this.refs.wrapper.addEventListener('wheel', e => this.handleWindowWheel(e), false);
  }

  componentWillUnmount() {
    this.refs.wrapper.removeEventListener('wheel', e => this.handleWindowWheel(e));
  }

  handleWindowWheel(e) {
    const { scrollHeight, clientHeight, scrollTop } = this.refs.scrollbars.getValues();
    // When the bottom is reached and we're scrolling down, prevent scrolling of the window
    if (e.deltaY > 0 && (scrollHeight - clientHeight <= scrollTop + 1)) e.preventDefault();
  }

  render() {
    return (
      <div>
        <Scrollbars ref='scrollbars'>
          <div ref='wrapper'>
          </div>
        </Scrollbars>
      </div>
    );
  }
}

I need the <div ref='wrapper'> because this.refs.scrollbars doesn't exist during mount, but if it is part of the code, it should be worn by the actual <Scrollbars /> component wrapper.

Sidenote:

If you get the following warning:

[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952

Unless I'm wrong, this is to be ignored, because this means one will never user event.preventDefault() which is not our case here. :smile:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leofle picture leofle  路  4Comments

hank7444 picture hank7444  路  5Comments

nindavidw picture nindavidw  路  4Comments

philcockfield picture philcockfield  路  3Comments

limbosounds picture limbosounds  路  4Comments