Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React - Unable to preventDefault inside passive event listener #11

Open
gaoryrt opened this issue Jul 20, 2019 · 5 comments
Open

React - Unable to preventDefault inside passive event listener #11

gaoryrt opened this issue Jul 20, 2019 · 5 comments

Comments

@gaoryrt
Copy link
Member

gaoryrt commented Jul 20, 2019

最近在用 react,hooks 配合 functional component 写起来非常的有禅意。

前两天遇见这个需求:
需要在自定义 prompt 出来的时候禁止页面滑动。

image

自然想到在 onTouchMove 和 onTouchStart 的时候去 preventDefault,
但是 Chrome 直接报错了:
Unable to preventDefault inside passive event listener

如果是 addEventListener('touchstart', fn, {passive: true}) 的 fn 里面尝试 preventDefault 就会报这个错。
事件监听时的 passive option 有什么用呢,能大幅提高滚动性能,前提是产品设计保证了不会阻止滚动。
所以应该是 react 在设置 onTouch* 系列监听的时候,设置了这个 passive
查了一下,原来是 chrome 56 开始的一个 feature

With this change touchstart and touchmove listeners added to the document will default to passive:true (so that calls to preventDefault will be ignored)..

@gaoryrt
Copy link
Member Author

gaoryrt commented Jul 20, 2019

参考 react 的讨论,还有对 chrome 的各种控诉
设置 css touch-action: none 去阻止所有,甚至是单个方向上的触摸操作

但是,iOS 对这个属性的支持不好:
image

所以只能手动绑定了

@gaoryrt
Copy link
Member Author

gaoryrt commented Jul 20, 2019

const ctnrEl = useRef(null)
const handleFn = e => e.preventDefault()
useEffect(() => {
  ctnrEl.addEventListener('touchmove', handleFn)
  return () => ctnrEl.removeEventListener('touchmove', handleFn, {passive: false})
}, [])
return <div ref={ctnrEl}>
	...
</div>

hooks 是真的禅

@GiorgioPeng
Copy link

请问一下阻止pc端滚动端话有方法么?
我参考你的代码写了发现监听scroll事件的时候并不能阻止

const wholePageRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    wholePageRef.current?.addEventListener('scroll', (e:any) => {
      e.preventDefault()
    })
  }, [])
  return (
    <div ref={wholePageRef}>
      ...
    </div>)

@gaoryrt
Copy link
Member Author

gaoryrt commented Mar 19, 2020

查了一下,由于 scroll 是 uievent 所以不能阻止,但是可以阻止产生 scroll 的事件比如 touchmove, DOMMouseScroll, wheel

来源 stackoverflow,文中的 demo 在我的 chrome ver81 已经不能阻止滚动了

新版 chrome 不支持的话,那可以采用最暴力的方法:记录当前滚动高度然后 display: fixed; height: 100vh; overflow: hidden,从 CSS 上阻止滚动

@GiorgioPeng
Copy link

好的,非常感谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants