zoukankan      html  css  js  c++  java
  • 由移动端页面点击穿透想到的

    原文链接 http://ymblog.net/2016/03/28/由移动端页面点击穿透想到的/

    首先我想到了哪些:

    1. 点击穿透是如何引起的
    2. 如何解决
    3. 什么是事件模拟

    一、点击穿透是如何引起的?

    可能是由click事件的延迟或者事件冒泡导致,事件包含touchstart/touchmove/touchend/mousedown/mousemove/mouseup/click。

    造成点击穿透的原因是,在移动端页面中click的点击事件有200-300ms的延迟,为了体验更好我们使用了zepto的touch事件,或者说使用的touchstart/touchend事件,在执行完 touch事件之后,目标事件执行,此时 click 事件还在延迟的 300ms 之中。当 300ms 到来的时候,click 到的其实是隐藏元素下方的元素。

    如果正下方的元素有绑定 click 事件,此时便会触发,如果没有绑定 click 事件的话就当没发生。如果正下方的是 input 输入框(或是 select / radio / checkbox),点击默认 focus 而弹出输入键盘,也就出现了上面的“点透”现象。

    那为什么要有延迟?浏览器在 touchend 后会等待约300ms,原因是判断用户是否有双击(double tap)行为。如果没有 tap 行为,则触发 click 事件,而双击过程中就不适合触发 click 事件了。由此可以看出 click 事件触发代表一轮触摸事件的结束。

    DOM事件流:

    1,事件捕获阶段;

    2,处于目标阶段;

    3,事件冒泡阶段。

    说到这儿我们再来了解下事件的执行顺序,在移动端页面中,touch事件的执行顺序是touchstart ->touchmove -> touchend -> mousedown ->mouseup ->click,当然在web端中可能顺序又不一样。

    那似乎上面说的穿透问题变得理所当然了。因为这是浏览器的默认行为! 

    二、如何解决

    一般来说加上下面的代码就会解决问题了。

    在touchend事件中阻止浏览器的默认行为,就可以了。

    event.preventDefault();
    

     

    上面的算是一种解决办法了,我记得还会有那种情况即使加了这行语句还是无效的更特殊的情况,有人说使用fastclick可以解决问题,当初我使用的时候发现确实好像点击快了很多,但是在华为荣耀3C的机型上却发现还是会出现点击穿透的现象,再考虑到为此我需要再引入一个文件的http的请求,所以果断放弃。

    还有一种治标不治本的方式,如下:

    由于 click 事件的滞后性,在这段时间内原来点击的元素消失了,于是便“穿透”了。因此我们顺着这个思路就想到,可以给元素的消失做一个fade效果,类似jQuery里的fadeOut,并设置动画duration大于300ms,这样当延迟的 click 触发时,就不会“穿透”到下方的元素了。

    同样的道理,不用延时动画,我们还可以动态地在触摸位置生成一个透明的元素,这样当上层元素消失而延迟的click来到时,它点击到的是那个透明的元素,也不会“穿透”到底下。在一定的timeout后再将生成的透明元素移除。

    $('#closePopup').on('tap', function(e){
        $('#popupLayer').hide();
        $('#bgMask').hide();
    
        $('#underLayer').css('pointer-events', 'none');
    
        setTimeout(function(){
            $('#underLayer').css('pointer-events', 'auto');
        }, 400);
    });
    

      

    但如果页面中的点击事件多了这样肯定不行的。

    三、什么是事件模拟

    zepto上的tap事件也是模拟出来的,我们似乎可以自己来模拟点击事件。

    我们可以打印控制台打印的event对象,

    `]FUU[}715{5KU7@LASSWCK

    所谓事件模拟就是通过特定的方法,传入指定的参数,然后派发这个自定义事件就可以了。

    var event = document.createEvent('MouseEvents');
    

      

    这个参数可选的有:

    1. UIEvents
    2. MouseEvents
    3. MutationEvents,一般化dom变动
    4. HTMLEvents一般dom事件
    var type = 'click'; //要触发的事件类型
    var bubbles = true; //事件是否可以冒泡
    var cancelable = true; //事件是否可以阻止浏览器默认事件
    var view = document.defaultView; //与事件关联的视图,该属性默认即可,不管
    var detail = 0;
    var screenX = 0;
    var screenY = 0;
    var clientX = 0;
    var clientY = 0;
    var ctrlKey = false; //是否按下ctrl
    var altKey = false; //是否按下alt
    var shiftKey = false;
    var metaKey = false;
    var button = 0;//表示按下哪一个鼠标键
    var relatedTarget = 0; //模拟mousemove或者out时候用到,与事件相关的对象
    
    var event = document.createEvent('MouseEvents');
    event.initMouseEvent(type, bubbles, cancelable, view, detail, screenX, screenY, clientX, clientY,
    ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
    

      

    最终的解决办法:

    var el = null;
    var list = document.getElementById('list');
    function getEvent(el, e, type) {
        e = e.changedTouches[0];
        var event = document.createEvent('MouseEvents');
        event.initMouseEvent(type, true, true, window, 1, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
        //event.forwardedTouchEvent = true;
        return event;
    }
    list.addEventListener('touchstart', function (e) {
        var firstTouch = e.touches[0]
        el = firstTouch.target;
    })
    list.addEventListener('touchend', function (e) {
        e.preventDefault();
        var event = getEvent(el, e, 'click');
        el.dispatchEvent(event);
    })
    list.addEventListener('click', function (e) {
        list.style.display = 'none';
        setTimeout(function () {
            list.style.display = '';
        }, 1000);
    })
    

      

    我们可以在touchend事件的时候阻止浏览器的默认行为,然后自己模拟事件的产生与触发。这就是自己创建一个click的Event对象触发之,所有的点击依然就用click。

    四、总结

    从早上看资料看到现在,刚开始思路还非常清晰,问题的来源,问题的产生等等以及该如何解决...看的时间久了,看的东西多了突然发现变得模糊了起来,发现很多东西都是冲突的,比如点击穿透是zepto引起的?看了下网友说的,由于zepto的模拟tap事件是冒泡到document才触发的,而在冒泡到 document 之前,手指接触和离开屏幕(touchstart / touchend)是会触发 click 事件的。所以就会有这个问题?那我阻止了浏览器的默认事件不就ok了?

    但似乎又是说在安卓和iOS的区别在于mousedown的执行速度,安卓大于iOS

    在android上获得的结果是惊人的,这个劳什子android里面moveover事件偶然比尼玛touchstart还快!!!

    而ios压根就不理睬mouseover事件,这是主要问题产生原因!!!

    而android在movedown时候,开开心心触发了input的focus事件,然后键盘就弹起来了!!!

    所以针对android,我们还得将mousedown干掉才行!!!!

    而事实上,我们input获取焦点,就是通过mousedown触发的,ios也是,所以要解决android下面的问题还得从其它层面抓起

    这也就产生了最终的解决办法:也就是上面写的,上面的只是针对一个点击事件,下面则针对全部:

    var touch = {};
    var t = new Date().getTime();
    document.addEventListener('click', function (event) {
            if (event.myclick == true) {
                return true;
            }
            if (event.stopImmediatePropagation) {
                event.stopImmediatePropagation();
            } else {
                event.propagationStopped = true;
            }
            event.stopPropagation();
            event.preventDefault();
            return true;
        }, true);
    
        document.addEventListener('touchstart', function (e) {
            touch.startTime = e.timeStamp;
            touch.el = e.target;
            t = e.timeStamp;
        });
        document.addEventListener('touchmove', function (e) { });
        document.addEventListener('touchend', function (e) {
            touch.last = e.timeStamp;
            var event = document.createEvent('Events');
            event.initEvent('click', true, true, window, 1, e.changedTouches[0].screenX, e.changedTouches[0].screenY, e.changedTouches[0].clientX, e.changedTouches[0].clientY, false, false, false, false, 0, null);
            event.myclick = true;
            touch.el && touch.el.dispatchEvent(event);
            return true;
        });
    
        dom.addEventListener('click', function (e) {
            alert(1);
        });
    

      

    其核心是自己模拟事件并派发。

    var event = document.createEvent('Events');
    event.initEvent('click', true, true, window, 1, e.changedTouches[0].screenX,
    e.changedTouches[0].screenY, e.changedTouches[0].clientX, e.changedTouches[0].clientY, false, false, false, false, 0, null);
    event.myclick = true;
    touch.el && touch.el.dispatchEvent(event);

    五、参考

    最先点开的这篇文章:https://segmentfault.com/q/1010000000691822

    后来又点开了这篇文章:https://segmentfault.com/a/1190000003848737

    然后在这篇文章的页脚又看到了很多,于是乎又查看了这篇:http://www.cnblogs.com/yexiaochai/p/3462657.html

    然后又看了这篇:http://www.cnblogs.com/yexiaochai/p/3442220.html

  • 相关阅读:
    Python自然语言处理读书笔记第7章
    [转]基于SNS的文本数据挖掘
    使用python多线程实现一个简单spider
    基于内存共享的并行排序算法慢谈(中)
    [转]为什么我反对纯算法面试题
    从客户端检测到有潜在危险的Request.Form值
    repeater 回发或回调参数无效
    Nginx安装、平滑升级与虚拟机配置
    log4j.properties的配置示例
    jquery一般方法介绍
  • 原文地址:https://www.cnblogs.com/jhmydear/p/5701005.html
Copyright © 2011-2022 走看看