移动端点击穿透

 阅读约 18 分钟

移动端触屏事件

移动端的开发经常需要监听用户的touch行为,在移动端主要有以下几个touch事件:

  • touchstart 手指触摸屏幕时触发,即使已经有手指在屏幕上也会触发
  • touchmove 手指在屏幕滑动时触发
  • touchend 手指从屏幕时移开时触发
  • touchcancel 当触控点被特定的实现方式打乱时触发(例如,弹框),一般不用

    移动端的事件的发生顺序一般是:touchstart---touchmove---touchend,然后大约过300ms会模拟鼠标触发click事件

什么是点击穿透

在发生触摸动作约300ms之后,移动端会模拟产生click动作,它底下的具有点击特性的元素也会被触发,这种现象称为点击穿透。

发生的条件:
上层元素监听了触摸事件,触摸之后该层元素消失
下层元素具有点击特性(监听了click事件或默认的特性(a标签、input、button标签))

解决点击穿透的方法

//点击穿透布局
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>移动端点击穿透</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        html,body {
            width: 100%;
            height: 100%;
        }
        #modalBg {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,.6);
        }
        .modal {
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            margin: auto;
            width: 300px;
            height: 100px;
            padding: 20px 0;
            background-color: #fff;
            text-align: center;
        }
        .modal p {
            margin: 20px 0;
        }
        .modal button {
            font-size: 16px;
            padding:10px 20px;
            border: 1px solid #ccc;
            background: #f5f5f5;
        }
        .links {
            margin-top: 250px;
        }
    </style>
</head>
<body>
    <div class="links">    //下层超链接元素
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
        <a href="http://www.baidu.com">百度一下你就知道了</a>
    </div>
    <div id="modalBg">    //上层元素
        <div class="modal">
            <p>操作成功</p>
            <button id="closeBtn">关闭</button>
        </div>
    </div>

1、阻止上层元素的默认动作

    <script>
        (function () {
            //获取弹框
            var modalBg = document.querySelector('#modalBg');
            var closeBtn = document.querySelector('#closeBtn');

            //按钮触摸事件 touchend
            closeBtn.addEventListener('touchend', function(event){
                modalBg.remove();

                //阻止默认行为
                event.preventDefault();
            });
        })();
    </script>
</body>
</html>

2、全局阻止默认行为

移动端阻止浏览器默认行为的方法:
1、在document元素内阻止,给addEventListener设置第三参数
2、自定义包裹元素,所有的内容都在包裹元素中,在包裹元素中阻止默认行为

addEventListener的第三个参数:

1、布尔值(true/false) 事件是否在捕获阶段触发
2、对象,接受三个属性

{
    passive:false,        //阻止默认行为
    capture:false/true,   //事件是否在捕获阶段触发
    once:false/true,      //事件是否只触发一次
}
//全局阻止默认行为
<script>
        (function () {
            //获取弹框
            var modalBg = document.querySelector('#modalBg');
            var closeBtn = document.querySelector('#closeBtn');

            //全局阻止默认行为
            document.addEventListener('touchstart', function(event){
                event.preventDefault();
            }, {passive: false});

            //按钮触摸事件 touchend
            closeBtn.addEventListener('touchend', function(event){
                modalBg.remove();
            });

            //单独给a元素添加 touchend事件
            var linkNodes = document.querySelectorAll('.links a');
            linkNodes.forEach(function(linkNode){
                linkNode.addEventListener('touchend', function(){
                   location.href =  this.href;
                });
            })
        })();
    </script>

3、不使用具有点击特性的元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>移动端点击穿透</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        html,body {
            width: 100%;
            height: 100%;
        }
        #modalBg {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,.6);
        }
        .modal {
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            margin: auto;
            width: 300px;
            height: 100px;
            padding: 20px 0;
            background-color: #fff;
            text-align: center;
        }
        .modal p {
            margin: 20px 0;
        }
        .modal button {
            font-size: 16px;
            padding:10px 20px;
            border: 1px solid #ccc;
            background: #f5f5f5;
        }
        .links {
            margin-top: 250px;
        }
    </style>
</head>
<body>
    <div class="links">    //下层超链接元素
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div> 
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div> 
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div> 
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div> 
         <div href="http://www.baidu.com">百度一下你就知道了</div>
         <div href="http://www.baidu.com">百度一下你就知道了</div> 
         <div href="http://www.baidu.com">百度一下你就知道了</div>
    </div>
    <div id="modalBg">    //上层元素
        <div class="modal">
            <p>操作成功</p>
            <button id="closeBtn">关闭</button>
        </div>
    </div>
<script>
        (function () {
            //获取弹框
            var modalBg = document.querySelector('#modalBg');
            var closeBtn = document.querySelector('#closeBtn');


            //按钮触摸事件 touchend
            closeBtn.addEventListener('touchend', function(event){
                modalBg.remove();
            });

            //单独给div元素添加 touchend事件
            var linkNodes = document.querySelectorAll('.links div');
            linkNodes.forEach(function(linkNode){
                linkNode.addEventListener('touchend', function(){
                   location.href =  this.getAttribute('href');
                });
            })
        })();
    </script>

4、利用css属性pointer-events

pointer-events属性有很多值,但是对于浏览器来说,只有auto和non两个值可用,其它的几个是针对SVG的(本身这个属性就来自于SVG技术)。

pointer-events属性值详解
  • auto——效果和没有定义pointer-events属性相同,鼠标不会穿透当前层。在SVG中,该值和visiblePainted的效果相同。
  • none——元素不再是鼠标事件的目标,鼠标不再监听当前层而去监听下面的层中的元素。但是如果它的子元素设置了pointer-events为其它值,比如auto,鼠标还是会监听这个子元素的。
  • 其它属性值为SVG专用,这里不再多介绍了。
<script>
        (function () {
            //获取弹框
            var modalBg = document.querySelector('#modalBg');
            var closeBtn = document.querySelector('#closeBtn');
            var aNodes = document.querySelectorAll('.links a');

            //按钮触摸事件 touchend
            closeBtn.addEventListener('touchend', function(event){
                //使所有的超链接失去点击特性
                aNodes.forEach(function(aNode){
                    aNode.style.pointerEvents = 'none';
                });
                modalBg.remove();

                //当300ms之后,模拟的click动作发生之后,再让超链接具备点击特性
                setTimeout(function(){
                    aNodes.forEach(function(aNode){
                        aNode.style.pointerEvents = 'auto';
                    });
                }, 350);
            });
        })();
    </script>

5、让上层元素不立即消失,等到模拟click动作约300ms之后再消失

<script>
        (function () {
            //获取弹框
            var modalBg = document.querySelector('#modalBg');
            var closeBtn = document.querySelector('#closeBtn');
            var aNodes = document.querySelectorAll('.links a');
            //按钮触摸事件 touchend
            closeBtn.addEventListener('touchend', function(event){
                //等一会再消失
                setTimeout(function(){
                    modalBg.remove();
                }, 350);
            });
        })();
    </script>

如果对您有帮助的话,点击 http://learn.fuming.site/ 学习更多!

阅读 225发布于 12月3日
推荐阅读
目录