PM需求是在h5页面上有一个可以拖拽的video,效果类似于ios AssistiveTouch,如下图所示:
在完成这个需求的时候,采用了React的Touch Event来对这种拖拽效果进行计算。部分代码如下
在该video元素的dom上使用onTouchxxx绑定了相关事件,结果发现后面的页面也跟着发生了滚动,效果如下:
于是就跟以前一样想利用e.preventDefault()方法来禁止父级元素跟着滚动,代码如下
结果在chrome的dev-tools里面发现了大量warning,而且在手机端上并没有出现理想的效果,warning如下图:
通过搜索相关的warning结果发现原因为以下信息
1 | AddEventListenerOptions defaults passive to false. With this change touchstart and touchmove listeners added to the document will default to passive:true (so that calls to preventDefault will be ignored).. |
根据 chrome 的提示得知,为了提高页面滚动的性能, Chrome 现在默认把通过在 document 上绑定的事件监听器 passive 属性(后面细说)默认置为 true,这样就会导致设置的 e.preventDefault() 被忽视了。为了防止带来的副作用,官方考虑的很周到,给我们提供了一个 CSS 属性专门用来解决这个问题
1 | touch-action: none; |
在加上了这个属性之后,安卓机确实按照需求的样子执行了,感觉应该能行。可是在ios中,移动video元素的时候父级元素还是在不停的滚动,并没有发生变化,兼容问题令人头疼~
查询了相关的兼容性之后才发现,在ios上对该属性只是部分兼容。所以只能考虑别的方法,由于前面说的产生这样的原因是因为chrome默认把passive置成了true,导致e.preventDefault()被忽略了。所以只能想办法把这个值置为false。查询相关文档后,React提供的合成事件没有提供传递该参数的方法,所以只能降级使用原生的事件绑定方式。查询MDN后发现addEventListener提供了修改该参数的方法:
解决方法:
然后就解决了该问题,实现效果如文档开始展现的那样。以后碰到类似的情况,可以用这种方式解决。在解决该问题的过程中,一开始以为是一个通用的问题,所以开始搜索的时候都是搜索的如何避免页面穿透这样的思路,然后试了好多方法,比如将父级元素的position定位fixed,又或者强行截断冒泡事件的执行,在页面上用scrollTop将值始终置为0,最终发现效果都不太好。最后发现是chrome针对页面滚动而采取的措施,开始搜索的方向就有点偏,稍微浪费了点时间。吃一堑长一智,以后还是要多多写代码,积累相关的经验。。。