Skip to content

点击穿透 ​

本节目标:理解“点击穿透”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 点击浮层关闭按钮时,也点击了页面上对应位置的元素(正常是不应该点击到页面上的对应的元素的) 原因 触屏设备为了区分用户双击缩放,对 click 做了 300ms 延迟触发,其在移动端的操作流程为

  1. 手指触碰到屏幕时,触发 touchstart , 弹窗隐藏
  2. 手指按上时,可能会有短暂的停留和轻微的移动,触发 touchmove
  3. 手指离开屏幕时,触发 touchend
  4. 等待 300ms 后,看用户在此时间内是否再次触摸屏幕,如果没有
  5. 300ms 后,此时弹窗已消失,浏览器在用户手指离开的位置触发 click 事件,所以点到了页面上的元素

解决方案 ❌方案1,增加 <meta name="viewport" content="device-width"> 虽然解决了 300ms 延迟,但是依然存在穿透现象,在 touchstart 里绑定了关闭弹窗,而 click 事件是在 touchend 事件之后(touchstart --> touchend --> click) 此时弹窗已消失,浏览器自动触发 click 事件,依然点到了页面上的相应位置。 测试

方案2 关闭弹窗时绑定的事件由 ontouchstart 变为 click ,因为(touchstart --> touchend --> click)手指离开时,会创建 click 时,就会有响应(touchstart --> touchend 事件虽创建了,但是没绑定,所以也没有响应),弹窗隐藏,这个 click 事件响应到了弹窗上的关闭按钮, <script> const $ = s => document.querySelector(s) const log = str => $('.log').innerText = str let i = 1 $('.close').onclick = (e) => { //ontouchstart 改为 onclick $('.mask').style.display = 'none' log('touched' + (++i)) } $('.bg').onclick = () => { log('点击了背景') } </script> 事件往上冒泡,但不会到 'bg' 上(是同级),会到 body 上,所以不会有穿透现象了 <span class="bg">背景</span> <p class="log">0</p> <div class="mask"> <span class="close">X</span> </div> 测试

方案3 弹窗关闭的时间延长,比如让弹窗 300ms 以上渐变消失,此时 click 创建,点到这个位置时,点的还是弹窗,因为弹窗还没有消失,click 到了弹窗上,弹窗上没有绑定的事件,什么也不做,所以 click 到不了页面上对应的位置 <style> .fade { animation: fade .3s; animation-fill-mode: forwards; } .hide { display: none; } @keyframes fade { 0% { opacity: 100%; } 100% { opacity: 0; } } </style> <script> const $ = s => document.querySelector(s) const log = str => $('.log').innerText = str let i = 1 $('.close').ontouchstart = (e) => { log('touched' + (++i)) } $('.close').ontouchend = e => { $('.mask').classList.add('fade') //增加渐变动画 setTimeout(() => $('.mask').classList.add('hide'), 300) } $('.bg').onclick = () => { log('点击了背景') } </script> 测试

方案4 在 ontouchstart 里阻止默认事件,会阻止 默认 click 事件的创建,自然也不会响应到页面上的元素 <script> const $ = s => document.querySelector(s) const log = str => $('.log').innerText = str let i = 1 $('.close').ontouchstart = (e) => { e.preventDefault() //阻止默认事件 $('.mask').style.display = 'none' log('touched' + (++i)) } $('.bg').onclick = () => { log('点击了背景') } </script> 测试

发布于 2019-09-02 > 来自 <https://zhuanlan.zhihu.com/p/80784312>

本站总访问 — 次