Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel wheel event with e.preventDefault in react event bubbling

I am trying to prevent window scrolling with e.preventDefault() when a wheel event is fired but browser scrolls no matter what.

<div onWheel={this.handleWheelEvent} >
  <div className='yellow fullpage'>Yellow Page</div>
  <div className='green fullpage'>Green Page</div>
  <div className='blue fullpage'>Yellow Page</div>
</div>

js

handleWheelEvent = e => {
    e.preventDefault();
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    // those 3 should prevent browser from scrolling but they don't
}
like image 966
Femi Oni Avatar asked Aug 05 '19 12:08

Femi Oni


People also ask

How does React handle event bubbling?

Event bubbling in React refers to when the innermost component handles an event, and events bubble outwards. In React, the innermost element will first be able to handle the event, and then surrounding elements will then be able to handle it themselves.

How do you stop a propagation of an event in React?

event.stopPropagation() This will stop any parent component's event from firing. To use this: Make sure to pass the event object as a parameter. Use the stopPropagation method on the event object above your code within your event handler function.

What is e preventDefault () in React?

The preventDefault() method cancels the event if it is cancelable, meaning that the default action that belongs to the event will not occur. For example, this can be useful when: Clicking on a "Submit" button, prevent it from submitting a form.

What is SyntheticEvent?

As a unified cross-browser wrapper around the browser's native events, React SyntheticEvent provides a unified API, prevents browser inconsistencies, and ensures that the event works across multiple platforms.


2 Answers

I couldn't find a way to prevent scrolling in React events but it's possible to achieve same effect using refs

In constructor:

this.divRef = React.createRef()
this.preventDefault = e => e.preventDefault()

In render:

<div ref={this.divRef} >
  <div className='yellow fullpage'>Yellow Page</div>
  <div className='green fullpage'>Green Page</div>
  <div className='blue fullpage'>Yellow Page</div>
</div>

Adding and removing preventDefault event listener:

componentDidMount () {
    this.divRef.current.addEventListener('wheel', this.preventDefault)
}

componentWillUnmount () {
    this.divRef.current.removeEventListener('wheel', this.preventDefault)
}
like image 183
Remzi Atay Avatar answered Oct 15 '22 06:10

Remzi Atay


React binds all events at the root element (not the document), and the wheel event is binded internally using true option, and I quote MDN:

A Boolean that, if true, indicates that the function specified by listener will never call preventDefault(). If a passive listener does call preventDefault(), the user agent will do nothing other than generate a console warning

This is how I did blocked the page from scrolling while using the wheel to affect an input field:

const wheelTimeout = useRef()

const onWheel = e => {
    // ... some code I needed ...

    // while wheel is moving, do not release the lock
    clearTimeout(wheelTimeout.current)

    // flag indicating to lock page scrolling (setTimeout returns a number)
    wheelTimeout.current = setTimeout(() => {
      wheelTimeout.current = false
    }, 300)
}

// block the body from scrolling (or any other element)
useEffect(() => {
    const cancelWheel = e => wheelTimeout.current && e.preventDefault()
    document.body.addEventListener('wheel', cancelWheel, {passive:false})
    return () => document.body.removeEventListener('wheel', cancelWheel)
}, [])

Reference discussions:

  • https://github.com/facebook/react/issues/14856
like image 43
vsync Avatar answered Oct 15 '22 05:10

vsync