zoukankan      html  css  js  c++  java
  • 移动端网页点击延迟及事件穿透

    一、click事件的300毫秒延迟是怎么产生的?

    苹果在2007年发布iphone前夕遇到一个问题,当时相应的网页都是针对大屏编写的,如果直接在iphone上浏览则会出现横向的滚动条,于是他们制定了一些规则来缩放网页,通过双击可以还原网页大小,这种方法后来被其它移动浏览器厂商所采用。300毫秒延迟产生的原因就在于当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。

    相应的解决办法

    1.禁用缩放

    <meta name="viewport" content="width=device-width, user-scalable=no">
     
    声明这个页面是不可缩放的,那双击缩放的功能就没有意义了,此时浏览器可以禁用默认的双击缩放行为并且去掉300ms的点击延迟。
    这个方案有一个缺点,就是必须通过完全禁用缩放来达到去掉点击延迟的目的,然而完全禁用缩放并不是我们的初衷,我们只是想禁掉默认的双击缩放行为,这样就不用等待300ms来判断当前操作是否是双击。但是通常情况下,我们还是希望页面能通过双指缩放来进行缩放操作,比如放大一张图片,放大一段很小的文字。
     
    2.更改默认的视口宽度
    <meta name="viewport" content="width=device-width">

    为了让桌面站点能在移动端浏览器正常显示,移动端浏览器默认的视口宽度并不等于设备浏览器视窗宽度,而是要比设备浏览器视窗宽度大,通常是980px。我们可以通过上面的标签来设置视口宽度为设备宽度。那浏览器就可以认为该网站已经对移动端做过了适配和优化,就无需双击缩放操作了。
    这个方案相比方案一的好处在于,它没有完全禁用缩放,而只是禁用了浏览器默认的双击缩放行为,但用户仍然可以通过双指缩放操作来缩放页面。

    3.css3的touch-action

    touch-action这个CSS属性。这个属性指定了相应元素上能够触发的浏览器的默认行为。文档说明

    html {
        -ms-touch-action: manipulation;
        touch-action: manipulation;
    }  

    该方案只支持移动端的chromiun 和 iOS 9.3+

    4、使用fast click库

    fastclick的实现思路是使用touchend事件来模拟click,不推荐该方法,该方法在特定场景下会导致事件穿透。

    FastClick.prototype.onTouchEnd = function(event){
    
      // 一些状态监测代码 
    
      // 从这里开始,
      if (!this.needsClick(targetElement)) {
        // 如果这不是一个需要使用原生click的元素,则屏蔽原生事件,避免触发两次click
        event.preventDefault(); 
        // 触发一次模拟的click
        this.sendClick(targetElement, event);
      }
    }
    FastClick.prototype.sendClick = function(targetElement, event) {
    
      // 这里是一些状态检查逻辑
    
      // 创建一个鼠标事件
      clickEvent = document.createEvent('MouseEvents');
      // 初始化鼠标事件为click事件
      clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
    
      // fastclick的内部变量,用来识别click事件是原生还是模拟
      clickEvent.forwardedTouchEvent = true;
    
      // 在目标元素上触发该鼠标事件,
      targetElement.dispatchEvent(clickEvent);
    }
    

    二、事件穿透

    1.现象描述

    假如页面上有两个元素A和B。B元素在A元素之上。我们给B元素的touchstart或touchend绑定一个回调函数,该回调函数的作用是隐藏B元素,当触摸B元素,B元素被隐藏了,随后,A元素触发了click事件。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <style>
       .mask{
         width: 200px;
         height: 100px;
         background-color: gold;
       }
       .top{
         width: 100px;
         height:100px;
         background-color: gray;
         position: absolute;
         top:0;
         left: 0;
       }
      </style>
    </head>
    <body>
    <div class="mask" id="bottom">我是底层</div>
    <div class="top" id="top">我是上层</div>
    <script>
      window.onload=function(){
        document.getElementById("bottom").onclick=function(){
          alert('我是底层');
        }
        document.getElementById("top").ontouchend=function(){
           this.style.display='none';
        }
      }
    </script>
    </body>
    </html>

     2.产生的条件

    上层元素采用touchstart或touchend,下层元素有click事件或是一个指定了href的超链接,上层元素在触摸后不可见

    3.产生的原因

     在移动端浏览器,事件执行的顺序是touchstart > touchend > click,touch事件结束后会等待300毫秒,然后执行click事件。

    上面的例子中touchend把上层元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时上层元素不见了,所以该事件被派发到了底层元素身上。如果底层元素是一个链接,那此时页面就会意外地跳转。

    4.如何解决

    网上搜到的meta头禁用缩放,css3的touch-action这两种方法都是瞎扯蛋,经实验,可行的方法如下:

    4.1 统一使用touch事件( touchstart 、’touchend’),然后上层元素隐藏后使用event.preventDefault(),要不然下层元素如果是a标签,a标签的href也是click行为

    4.2 如果不需要判断触摸手势等,可以把页面内所有的touch换成click,然后采用第一节中的方案来解决click延迟

    4.3 遮挡法:动态地在触摸位置生成一个透明的元素,这样当上层元素消失而延迟的click来到时,它点击到的是那个透明的元素,也不会“穿透”到底下

    4.4 延迟法:给消失的元素做一个fade效果,类似jQuery里的fadeOut,并设置动画duration大于300ms,这样当延迟的 click 触发时,就不会“穿透”到下方的元素了。

    4.5下层元素使用pointer-events属性,上层元素隐藏时将下层元素的pointer-events设为none,然后400ms后把该属性恢复成auto。该方法的问题在于必须要知道可能受影响的下层元素有哪些,实际项目中意义不大

      window.onload=function(){
        document.getElementById("bottom").onclick=function(){
          alert('我是底层');
        }
        document.getElementById("top").ontouchend=function(e){
           this.style.cssText="display:none;"
           var bottom=document.getElementById("bottom");
           bottom.style.cssText="pointer-events:none";
           setTimeout(function(){
            bottom.style.cssText="pointer-events:auto";
           }, 400);
        }
      }
  • 相关阅读:
    【转】C++轻量级可配置语法分析器
    [转载]正则表达式大全
    Batch update returned unexpected row count from update 错误解决方法
    [转载]C# ToString格式字符串整理(Format)(数字、日期和枚举的标准格式设置说明符)(SamWang)
    Centos配置mono环境
    ASP.NET MVC 4 简介
    添加控制器 Adding a Controller
    [转载]OrmHate
    [转载]张小龙谈移动互联网产品
    [转载]Golden Ratio in logo designs
  • 原文地址:https://www.cnblogs.com/94pm/p/9142009.html
Copyright © 2011-2022 走看看