移动端事件穿透的原理与解决方案

时间: 2020-07-24阅读: 150标签: 移动端

移动设备的流行,带动了移动互联网的快速发展,很多开发者开始进入移动开发领域。目前市面上主流的移动设备一般都使用触摸屏,触摸屏所使用的触摸事件模型与传统网页的鼠标事件模型有所区别,这种差异往往使初涉移动端的开发工程师陷入困境,事件穿透问题便是其中一个,本文将带你了解事件穿透及如何在实际项目中选择合适的方案解决事件穿透问题。


产生的原因

当今,主流的移动设备一般都使用触摸屏,Web 应用程序可以使用触摸事件(Touch Events)直接处理基于触摸的输入,或者应用程序可以使用可解释的鼠标事件以处理应用程序的输入。使用鼠标事件的缺点是它们不支持并发用户输入,而触摸事件支持多个同时输入(可能在触摸面上的不同位置),从而增强用户体验。

触摸事件有以下事件类型:

  • touchstart:当触摸点放置在触摸面上时触发。
  • touchmove:当触摸点沿触摸表面移动时触发。
  • touchend:当触摸点从触摸表面移除时触发。
  • touchcancel:当触摸点以实现特定的方式中断(例如,创建的触摸点太多)时触发。

在很多情况下,触摸事件和鼠标事件会同时被触发(目的是让没有对触摸设备优化的代码仍然可以在触摸设备上正常工作)。如下代码

document.addEventListener('touchstart', () => {
  console.log('touchstart')
})

document.addEventListener('touchend', () => {
  console.log('touchend')
})

document.addEventListener('click', () => {
  console.log('click')
})

事件触发的先后顺序是:touchstart -> touchend -> click。正是由于这种 click 事件的滞后性设计为事件穿透(点击穿透)埋下了伏笔。


什么是事件穿透

事件穿透是指触发某个目标元素的触摸事件时,会同时触发该目标元素相同位置中其他元素的鼠标点击事件。例如:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>事件穿透</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      div {
        width: 100vw;
        height: 100vh;
        line-height: 100vh;
        text-align: center;
      }

      .mask {
        position: fixed;
        top: 0;
        left: 0;
        background: #333;
        opacity: 0.6;
      }
    </style>
  </head>
  <body>
    <div>事件穿透</div>
    <div class="mask"></div>

    <script>
      const $div = document.querySelector("div")
      const $mask = document.querySelector(".mask")

      $mask.addEventListener('touchstart', (e) => {
        console.log('mask touchstart')
        e.target.style.display = 'none'
      })

      $div.addEventListener('click', () => {
          console.log('div click')
        })
    </script>
  </body>
</html>

由于 mask 元素触发 touchstart 触摸事件并立即隐藏掉自身,之后应该按先后顺序触发 mask 元素的 touchend 和 click 事件。然而,当要触发 click 事件的时候由于 mask 元素已经隐藏掉了,于是触发了 div 的 click 事件。

常见的事件穿透场景:

  • 目标元素触发触摸事件时隐藏或移除自身,对应位置元素触发 click 事件或 a 链接跳转。
  • 目标元素使用触摸事件跳转至新页面,新页面中对应位置元素触发 click 事件或 a 链接跳转。
注意:a 标签的链接跳转事件属于 click 事件。


解决方法

市面上解决事件穿透的方法有很多,大致可以分为两类:第一种是禁止混用 click 和 touch 两种事件;另一种是延迟元素的隐藏或移除。


禁用 click 事件

这种方法是将页面内所有元素的 click 事件改用 touch 事件。这种方法的好处非常明显,既解决了 click 事件延迟造成体验不佳的问题又解决了事件穿透的问题,但是缺点也很明显,就是 a 标签的链接跳转的处理问题。

禁用 a 标签的点击事件,改用 touch 事件触发链接跳转。实现如下:

// 禁用 a 标签的点击事件
document.addEventListener('click', (e) => {
  const href = e.target.getAttribute('href')
  const nodeName = e.target.nodeName.toLowerCase()

  if (nodeName === 'a' && href) {
    e.preventDefault()
  }
})

// 改用 touch 事件触发链接跳转
document.addEventListener('touchstart', (e) => {
  const href = e.target.getAttribute('href')
  const nodeName = e.target.nodeName.toLowerCase()

  if (nodeName === 'a' && href) {
    const target = e.target.getAttribute('target')
    window.open(href, target || '_self')
  }
})

看似很完美,然而,当 a 标签内包含后带元素的时候,后代元素的 click 事件通过冒泡还是会触发 a 标签的跳转。怎么解决?使用 pointer-events 禁用 a 标签所有后代元素的鼠标事件:

a[href] * {
  pointer-events: none;
}


禁用 touch 事件

这种方法是将页面内所有元素的 touch 事件改用 click 事件。事件穿透不就是由于 touch 与 click 事件存在触发时间差造成的吗,全部都使用 click 事件就不会有问题。然而事实真的如此美好?当然不是的,首先要解决 click 事件延迟 300ms 的问题。解决点击事件延迟的问题可以使用以下的 css 代码实现:

html {
  touch-action: manipulation;
}

这样已经很完美了。然而,什么是工作?工作就是不停的解决问题。当你不得不为项目添加手势功能,增加用户体验的时候(比如:左滑、右滑等等各种滑),你才会意识到完全禁用 touch 事件在实际项目中是不可能的事情。这个时候怎么办,推到从来,全部改用 touch 事件?当然不用这么麻烦,你可以在使用 touch 事件时通过调用 preventDefault() 阻止触发 click 事件。例如:

const $mask = document.querySelector(".mask")

$mask.addEventListener('touchstart', (e) => {
  ...
  e.preventDefault()
})


总结

解决事件穿透还有通过设置动画过渡延迟元素消失等方法,由于这类方法影响用户体验,不一一介绍。在实际项目开发中,纯移动端项目优先推荐禁用 click 事件的方法,多端项目优先推荐禁用 touch 事件的方法。

原文:https://www.cnblogs.com/jofun/archive/2020/07/24/13371431.html

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/9510

关闭

移动web问题小结

本文主要收集一些移动web开发中常见的问题和解决办法,在日常的工作中遇到新的问题会不定时更新到文章中。屏蔽阴影、Meta标签、获取滚动条的值、禁止选择文本、css之border-box、Retina屏幕高清图片、html5重力感应事件、移动端touch事件

移动端H5页面开发坑点指南

在平时的H5移动端开发时,我们难免会遇到各种各样的坑点,这篇文章就带着大家来看看怎么解决,文章较长,建议收藏方便以后查阅!canvas在retina屏模糊只需要将画笔根据像素比缩放即可

解决移动端1px边框最好的方法

在移动端开发时,经常会遇到在视网膜屏幕中元素边框变粗的问题。本文将带你探讨边框变粗问题的产生原因及介绍目前市面上最好的解决方法。

移动端实现表头固定,tbody滚动的三种方法

实现表头固定,tbody垂直滚动。准备工作:获取页面可是区域高度。方法一:两个table,第一个table放表头,第二个table方内容,通过JS实现监听滚动事件,动态控制表头位置

解决移动端点击穿透问题_h5实现移动端点击事件穿透的多种解决方案

移动端点透现象的解决办法:解决方案一:来得很直接github上有个fastclick可以完美解决;解决方案二:用touchend代替tap事件并阻止掉时A元素touchend的默认行为preventDefault(),从而阻止click事件的产生;解决方案三:延迟一定的时间(300ms+)来处理事件...

移动端如何强制页面横屏

有些机型有些app不能横屏:比如Android的微信就没有横屏模式,而ios的微信能开启横屏模式。解决办法就是在竖屏模式下,写一个横屏的div,然后设置rotate正(负)90度,把他旋转过来;而且如果用户切到横屏时,需要把rotate复原,要求也能正常展现。

vue中的适配:px2rem

这应该是vue项目在适配移动端时候,最简单的方法之一。下载并引入lib-flexible,引入px2rem-loader,将px2rem-loader添加到cssLoaders,完了就可以直接用px做单位按照750的设计稿撸代码了。

解决移动端禁止双指缩放功能

在实际开发中,我们禁止缩放的实现方式meta设置,user-scalabel=no或者user-scalabel=yes(yes是可以缩放,no或者0是不能缩放),在ios10以上的系统中,并不支持meta标签,需要我们通过脚本实现

js判断pc端和手机端

js判断是否为移动端代码,获取用户userAgent代理头的值,进行匹配判断,如果匹配到就进行跳转到移动端。查看当前代理头信息在浏览器中按F12进入调试模式,查看请求

移动端H5开发遇到的坑

微信分享签名错误invalid signature,往返缓存问题,IOS端不支持new Date(\"2019-01-01 00:00:00\") 这种格式,微信二维码,IOS中无法点击,audio音频无法播放,fixed问题

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全运营推广