Do you want to request a feature or report a bug?
Bug
What is the current behavior?
React attaches events to the document
node which causes the latest version of Chrome to issue the following warning.
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
It appears that Chrome now treats all document level touch events as passive.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).
class App extends Component {
render() {
return (
<div
className="box"
onTouchStart={e=>{e.preventDefault()}}
onTouchMove={e=>{e.preventDefault()}}
>
Drag Me
</div>
);
}
}
What is the expected behavior?
preventDefault
should be allowed, which means React should pass { passive: false }
when adding event listeners.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 15.4.1, Chrome 56.0.2924.87, Windows 10
I would link that issue with #6436.
@mstijak If you are looking for a workaround. You can manually use the addEventListener
and removeEventListener
DOM API with the right option.
@oliviertassinari Manually attaching listeners seems wrong. I think this should be fixed in React.
Here is the fiddle. To see the warning open the console and switch to the device mode (touch events).
Ok. This will be interesting to deal with. If Chrome makes a breaking change, does that mean libraries should respond by unbreaking it? Effectively undoing the purpose.
Now this whole thing will be complicated further by the fact that React Fiber is a whole shift to be able to write 60fps apps with active event listeners. In that world defaulting to active makes more sense.
On the flip side we're not quite there yet in the ecosystem and there is some action from apps to be able to make that palatable.
This will need some careful consideration to make the right choices for defaults.
Rather than set passive:false, it's much better to apply the appropriate touch-action to disable scrolling where desired. See here.
@RByers What would be the best thing to do if a component uses both mouse and touch events?
@RByers does that let you disable scrolling the outer window if you're at the bottom of an inner scrollable surface such as the right sidebar on Facebook? (Without disabling scrolling of the sidebar itself.)
@mstijak mouse and touch are orthogonal here. You can/should still call preventDefault
, just also set touch-action
to indicate declaratively what touch gestures you want disabled.
@sebmarkbage it can, in at least the same scenarios you can disable scrolling via TouchEvent.preventDefault in Safari. I.e. once scrolling has started you can't change it.
You may want to set pan-up
when at the bottom.
Relevant Chromium issue for context:
https://bugs.chromium.org/p/chromium/issues/detail?id=639227
Was this ever resolved?
No, it wasn't.
@RByers - "it's much better to apply the appropriate touch-action to disable scrolling where desired" ... yes, but this doesn't provide the flexibility that you can achieve using preventDefault. I'm working on an app, for instance, that allows scrolling around a list with a touch + move gesture, but switches to dragging a single item with touch + hold for 0.5s + move. Waiting to see whether a move arrives before the 0.5s and adding the touch-action style doesn't seem to work reliably (I think it ignores the touch action for the entire gesture if the first movement of the gesture is in a direction that can cause scrolling, but accepts it if the first movement is in a direction that doesn't cause scrolling).
@jh3141: Yes, that's absolutely true. I have a proposal for (what I think is) that exact use case here, I'd love your feedback there! If we get a few developers saying they need this and can agree on a design that would work, then I'm sure it's something we could ship sooner rather than later.
this is a blocker for enabling touch specific event handlers, as there is no way to prevent default mouse events from firing according to https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent
Will this be fixed in React 17.0? I noticed in the changes that events will be bound to "root element" instead of "document".
https://reactjs.org/blog/2020/08/10/react-v17-rc.html#changes-to-event-delegation
Yes, we've accidentally "fixed" this with that change in one of the RCs, but in the 17 we're explicily making onWheel
, onTouchStart
, and onTouchMove
passive to align with the browser behavior. Since this change has improved the scrolling performance on the web overall, we don't think it would be productive to "undo" the fix — as it's still likely going to lead to performance problems. For now, you can keep using refs + addEventListener
as a workaround if you need active event listeners.
Most helpful comment
Ok. This will be interesting to deal with. If Chrome makes a breaking change, does that mean libraries should respond by unbreaking it? Effectively undoing the purpose.
Now this whole thing will be complicated further by the fact that React Fiber is a whole shift to be able to write 60fps apps with active event listeners. In that world defaulting to active makes more sense.
On the flip side we're not quite there yet in the ecosystem and there is some action from apps to be able to make that palatable.
This will need some careful consideration to make the right choices for defaults.