• 提升手持设备点击速度之touch事件带来的坑!


    前言

    上周六,我将我们项目的click换成了tap事件,于是此事如梦魇一般折磨了我一星期!!!

    经过我前仆后继的努力,不计代价的牺牲,不断的埋坑填坑,再埋坑的动作,最后悲伤的发现touch事件确实是个坑!

    但是touch事件带来的用户感受提高对我们来说是一巨大进步,所以一些问题我们必须攻克,然在下已几近黔驴技穷,最后使出了浑身解数以一恶心的手段暂时压制其问题......

    现在分享被折磨过程,希望对各位有所帮助

    点击不起作用

    我使用的源码不是最新的,zepto初始化时便为document.body绑定touchstart、touchmove、touchend事件

    所以我们现在每一次在手机上的点击都会触发一次touch事件,由此可能引起的BUG:

     每次手指触屏都会触发touchstart等事件,可能堵塞浏览器本身行为

    由于我们是单页应用,初始化会为body设置height为100%,就算进入列表页或者其它,body页高度不会增加,于是就会出现不可点击现象!

    以去哪儿为例

    由于其城市列表为absolute,其它dom皆不显示从而导致body实际高度为0,那么此时如果为城市绑定tap事件的话,那么tap事件

    在ios手机上不会有作用,电脑上有用,某些android无效

    此问题较容易解决:

    ① 让body可伸缩,跟着元素扩展(不易实现)

    ② 将事件绑定至document(推荐)

    tap特有的点透现象

    地球人都知道tap会出现点透现象,我这里上一个代码给各位测试一番

     1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     2 <html xmlns="http://www.w3.org/1999/xhtml">
     3 <head>
     4     <title></title>
     5     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
     6     <style>
     7         #list { border: 1px solid black; position: absolute; top: 0px; left: 10px; width: 200px; height: 100px; }
     8         #d { border: 1px solid black; height: 300px; width: 100%; }
     9     </style>
    10 </head>
    11 <body>
    12     <div id="d">
    13         <input type="text" id="input" style=" 80px; height: 200px;" />
    14         <div id="list">
    15         </div>
    16     </div>
    17 </body>
    18 <script src="res/libs/zepto.js" type="text/javascript"></script>
    19 <script type="text/javascript">
    20     window.log = function (msg) {
    21         console.log(msg);
    22         var div = $('#myMsg');
    23         if (!div[0]) div = $('<div id="myMsg"></div>')
    24         $('#d').append(div);
    25         div.click(function () {
    26             div.html('');
    27         });
    28         div.append($('<div>' + msg + '</div>'));
    29     }
    30     var list = $('#list');
    31     var d = $('#d');
    32     var input = $('#input');
    33 
    34     input.tap(function (e) {
    35         input.val(new Date().getTime());
    36     });
    37 
    38     list.tap(function (e) {
    39         list.hide();
    40         setTimeout(function () {
    41             list.show();
    42         }, 1000);
    43 
    44     });
    45 
    46     d.tap(function () {
    47         log('div tap');
    48     });
    49 </script>
    50 </html>

    这个页面有三个元素

    ① 父容器div,我们为他绑定了一个tap事件,会打印文字

    ② 在上的div,我们为其绑定了一个tap事件,点击便消失

    ③ input,主要用于测试focus问题

    现在开启touch事件的情况下,我们点击上面的div,他会消失,于是:

    div消失会触发div(list)的tap事件

    div消失会触发input获取焦点事件

    可能导致的项目BUG

    以上问题可能导致些什么问题呢?

    提示层一闪而过

    我们可能会遇到这么一个场景:

    表单提交页,用户提交时如果信息有误,会弹出一个提示,并且为蒙版添加click的关闭事件

    但是有tap在的情况效果就不一样了,我们极有可能点击提交,弹出提示层,触发蒙版点击事件,蒙版关闭!!!

    input获取焦点弹出键盘

    我们可能遇到这种情况,我们在弹出层上做了一些操作后,点击弹出层关闭弹出层,但是下面有一个input(div有事件也行)

    于是触发了div事件,于是input获取了焦点,某明奇妙的弹出来键盘!!!

    BUG原因

    以上问题是主要出现的问题,由此问题可能衍生出其它BUG,足以让我们苦不堪言!!!

    但是引起这些问题的原因是什么呢???

    先说冒泡

    我们看zepto的touch事件源码,可以知道,我们一开始就将touchstart绑定到了document上,然后从e获取当前点击元素

    完了触发touchend模拟tap等事件。

    于是,首先我们想到了阻止冒泡是否可以解决问题:于是稍微修改下代码:

    1 list.tap(function (e) {
    2     list.hide();
    3     setTimeout(function () {
    4         list.show();
    5     }, 1000);
    6     e.stopPropagation();
    7 });

    事实证明,阻止冒泡后div(d)的tap事件不会被触发,但是input的获取焦点问题依旧不能解决

    至于原因,我这里也只能是猜测div(d)的触发是由于冒泡引起,而就算阻止冒泡阻止浏览器默认操作也不能阻止input获取焦点

    神奇菊花解决问题

    于是经过老夫不泄的努力,终于发现一个恶心的方法,可以暂缓此问题,他就是菊花!!!!

    先上个代码:

     1 //该代码在zepto touch源码中
     2 forTap = $('#forTap');
     3 if(!forTap[0]) { forTap = $('<div id="forTap" style="background: black;color: White; display: none; 
     4 border-radius: 60px; position: absolute; z-index: 99999;  60px; height: 60px"></div>');
     5     $('body').append(forTap);
     6 }
     7 
     8 //touchstart
     9 var el = touch.el; touch.isShowTap = false
    10 while(el[0].nodeName != 'BODY'){
    11 if(el.attr('lazyTap')) {
    12     touch.isShowTap = true;
    13     break;
    14     }
    15 el = el.parent();
    16 }
    17 
    18 //touchend
    19 var event = $.Event('tap')
    20 event.cancelTouch = cancelAll
    21 
    22 touch.el.trigger && touch.el.trigger(event)
    23 
    24 if(touch.isShowTap) {
    25     forTap.css({
    26         top: (e.changedTouches[0].pageY - 30) + 'px',
    27         left: (e.changedTouches[0].pageX - 30) + 'px'
    28     }) 
    29     forTap.show();
    30     setTimeout(function () {
    31         forTap.hide();
    32     }, 350);
    33 }

    大家注意看代码,我现在为我们需要延迟的元素加一个lazyTap的属性

    1 <div id="d">
    2     <input type="text" id="input"  style ="  80px; height: 200px;"/>
    3     <div id="list" lazyTap="true">
    4     </div>
    5 </div>

    于是我们现在点击div(d)时候就会马上有一朵菊花移动到他下面

    大家看到那个黑色的菊花了么???

    这朵菊花会跟着被设置了lazyTap的属性触发了tap后便不会引发下面的事件,于是把菊花的背景去掉,他就是一个隐藏的菊花了。。。。。。

    于是,到此为止吧!!!

    结语

    菊花带来的问题也有很大多,第一个就是不专业,第二个就是如果为li设置了lazyTap属性,那么在Ul上面绑定的事件会不会冒泡上去,我也没有验证!!

    但是,按道理应该被触发,所以!!!如果您有什么更好的解决方案,请务必留意!!!

  • 相关阅读:
    maven-scm-plugin: Add a tag into Mercurial. 在Mercurial中添加一个tag
    JSch
    docker-compose install
    如何删除Dead状态的container
    inux下如何添加一个用户并且让用户获得root权限
    Log4j2 自定义 Appender
    Harbor私有镜像仓库(上)
    docker基础(下)
    docker基础(上)
    Pipeline流水线JAVA项目发布
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/3391015.html
Copyright © 2020-2023  润新知