美文网首页Web前端之路让前端飞前端开发那些事
由event.target引发的关于事件流的一连串思考(二)

由event.target引发的关于事件流的一连串思考(二)

作者: Ruby君 | 来源:发表于2017-06-27 17:54 被阅读39次
阻止事件冒泡

W3C的方法是ev.stopPropagation(),IE则是使用ev.cancelBubble = true。

先不谈IE的私有方法,首先讨论一个问题:ev.stopPropagation()真的是阻止事件冒泡吗?

实际上,这个问题需要分两种情况来讨论:

  1. DOM0的阻止冒泡事件
    还是同心圆的例子,代码如下。
    JavaScript:
var div = document.querySelector("div");
var ul = document.querySelector("ul");
var li = document.querySelector("li");
div.onclick = function(ev){
    console.log("div");
}
ul.onclick = function(ev){
    console.log("ul");
}
li.onclick = function(ev){
    console.log("li");
    ev.stopPropagation();
}

此时输出结果如下:


DOM0阻止事件

可以看到阻止事件冒泡成功了,证明DOM0确实是阻止事件冒泡。

  1. DOM2的阻止冒泡事件
    还是同心圆的例子,代码如下:
var div = document.querySelector("div");
var ul = document.querySelector("ul");
var li = document.querySelector("li");
div.addEventListener('click',function(){
    console.log("div 捕获");
},true);
ul.addEventListener('click',function(ev){
    console.log("ul 捕获");
},true);
li.addEventListener('click',function(ev){
    console.log("li 捕获");
},true);
div.addEventListener('click',function(ev){
    console.log("div 冒泡");
});
ul.addEventListener('click',function(ev){
    console.log("ul 冒泡");
});
li.addEventListener('click',function(ev){
    console.log("li 冒泡");
});

此时点击内层圆,输出结果如下:


没有阻止事件冒泡的情况

我们将

ul.addEventListener('click',function(ev){
    console.log("ul 冒泡");
});

改为

ul.addEventListener('click',function(ev){
    console.log("ul 冒泡");
    ev.stopPropagation();
});

此时点击内层圆,输出结果如下:


阻止ul的事件冒泡

可以看到本来会冒泡到div上的点击事件被阻止了,此时跟DOM0的阻止事件冒泡是一致的。
但是如果我们改为阻止在ul的捕获事件上阻止事件冒泡的话,事件捕获还会进行吗?还是只会阻止冒泡?我们来测试一下,将

ul.addEventListener('click',function(ev){
    console.log("ul 捕获");
},true);

改为

ul.addEventListener('click',function(ev){
    console.log("ul 捕获");
    ev.stopPropagation();
},true);

此时的输出结果如下:


阻止ul的事件捕获

可以看到,不但冒泡的整个阶段被阻止了,而且li的事件捕获也被阻止了。

所以总结一下这两点,ev.stopPropagation()不止可以阻止事件冒泡,如果在捕获阶段使用还可以阻止事件捕获。

那么我们再考虑一种特殊情况:如果ev.stopPropagation()在target阶段执行会是什么情况,代码如下。
JavaScript:

div.addEventListener('click',function(){
    console.log("div 捕获");
},true);
ul.addEventListener('click',function(ev){
    console.log("ul 捕获");
},true);
li.addEventListener('click',function(ev){
    console.log("li 捕获");
    ev.stopPropagation();
},true);
div.addEventListener('click',function(ev){
    console.log("div 冒泡");
});
ul.addEventListener('click',function(ev){
    console.log("ul 冒泡");
});
li.addEventListener('click',function(ev){
    console.log("li 冒泡");
});

我们在target上阻止了事件监听,这样整个事件冒泡应该是被阻止的,我们看输出结果:


在target上阻止了事件监听

target的冒泡仍然执行了,其实原因很简单,我们在上一章已经提到了,就不多做赘述了。

阻止事件冒泡的兼容写法:

if(event.stopPropagation){
    event.stopPropagation();
}else{
    event.cancelBubble = true;
}
阻止默认事件

这个没什么好讲的,用于阻止浏览器默认的事件,比如提交按钮的点击默认提交,a标签的点击跳转等。不过之前遇到过一个问题,在jsp里无法阻止提交按钮的提交事件,最后还是换成了div然后使用form.submit()提交才通过。
兼容写法如下:

if(event.preventDefault){
    // W3C的阻止默认事件
    event.preventDefault();
}else{
    // IE私有的阻止默认事件
    event.returnValue = false;
}
事件流的常见应用:事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

阻止事件冒泡应用

有一个很应景的例子,页面有一个弹出框,可拖拽,弹出框内有一个input框,这个框内输入的文字要可以选中。
我们先来实现页面结构和拖拽功能,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        div{
            position: absolute;
            width: 300px;
            height: 100px;
            background-color: #2578b5;
            box-sizing: border-box;
            padding: 30px;
        }
        input{
            width: 100%;
            line-height: 1.6em;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div>
        <input type="text" value="用于复制的文本">
    </div>
    <script>
        var div = document.querySelector('div');
        var input = document.querySelector('input');
        div.onmousedown = function(ev){
            var mouseLeftDiv = ev.clientX - div.offsetLeft;
            var mouseTopDiv = ev.clientY - div.offsetTop;
            document.onmousemove = function(ev){
                var divLeftBody = ev.clientX - mouseLeftDiv;
                var divTopBody = ev.clientY - mouseTopDiv;
                var docWidth = document.documentElement.clientWidth;
                var docHeight = document.documentElement.clientHeight;
                if(divLeftBody <= 0){
                    divLeftBody = 0;
                }else if(divLeftBody >= docWidth - div.offsetWidth){
                    divLeftBody = docWidth - div.offsetWidth;
                };
                if(divTopBody <= 0){
                    divTopBody = 0;
                }else if(divTopBody >= docHeight - div.offsetHeight){
                    divTopBody = docHeight - div.offsetHeight;
                };
                div.style.left = divLeftBody + 'px';
                div.style.top = divTopBody + 'px';
            }
            document.onmouseup = function(){
                document.onmousemove = null;
            }
        }
    </script>
</body>
</html>

此时页面如下:


可拖拽页面

拖拽功能实现了,但是我们发现想要复制文本的时候,没有复制到,反而也触发了拖拽功能,这就很尴尬了。

这时我们的阻止事件冒泡就起作用了,我们在input框上阻止mousedown事件冒泡到外层,代码如下:
JavaScript:

input.onmousedown = function(ev){
    ev.stopPropagation();
}

大功告成,此时既可以复制文本,外层又可以拖拽。

参考资料:
http://www.cnblogs.com/liugang-vip/p/5616484.html
http://www.cnblogs.com/libin-1/p/6368323.html

相关文章

网友评论

    本文标题:由event.target引发的关于事件流的一连串思考(二)

    本文链接:https://www.haomeiwen.com/subject/uuuecxtx.html