DOM的事件-事件流·事件委托·取消事件冒泡和捕获
文章目录
前言
当有以下图片的内容时,如果我们用鼠标点击中间3方框
,是不是也同时点击了外面的1、2方框
,所以这个时候也会产生一个顺序的问题,那么究竟是以3->2->1
由内到外的触发形式?还是以1->2->3
由外到内的触发形式呢?故此前者就是事件的冒泡,后者为事件的捕获。
事件的冒泡(Bubble)
- 微软公司认为事件应该是由内向外传播,也就是当事件触发时,应该先触发当前元素上的事件,然后再向当前元素的祖先元素上传播,也就是说事件应该在冒泡阶段执行。
下面通过DOM0
的写法给元素 绑定事件处理回调函数来具体具体演示说明:
<div id="box1">
<div id="box2">点我!!!</div>
</div>
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var body = document.querySelector("body");
//给box1、box2、body、document、window分别绑定事件处理回调函数
box1.onclick = function () {
console.log("我是外面的green的Box!");
}
box2.onclick = function () {
console.log("我是里面的lightbule的Box!");
}
body.onclick = function () {
console.log("body");
}
document.onclick = function () {
console.log("document");
}
window.onclick = function () {
console.log("window");
}
</script>
下面是CSS样式部分
#box1 {
width: 300px;
height: 300px;
background-color: lightgreen;
overflow: hidden;//触发BFC
}
#box2 {
width: 100px;
height: 100px;
background-color: lightblue;
margin: 100px auto;
text-align: center;
line-height: 100px;
}
- 效果如下,当点击中间box时,会依次打印如下:
- 可以看见当前事件冒泡的顺序为:
lightbule -> green-> body -> window
由内向外传播的方式 - 在
ie9+(现代浏览器)
中所有事件会一直冒泡到window上 - 故此
ie8及以下
没有捕获阶段
DOM1
的写法addEventListener('事件名', 回调函数, false)
,其中第三个参数false
是默认开启事件冒泡,反之就是关闭事件冒泡:
box1.addEventListener('click',function(){
console.log("我是外面的green的Box!");
}, false);
box2.addEventListener('click',function(){
console.log("我是里面的lightbule的Box!");
}, false);
body.addEventListener('click',function(){
console.log("body");
}, false);
document.addEventListener('click',function(){
console.log("document");
}, false);
window.addEventListener('click',function(){
console.log("window");
}, false);
事件的捕获
- 网景公司认为事件应该是由外向内传播的,也就是当事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后再向内传播给后代元素
下面通过DOM1
中的addEventListener
的写法给元素 绑定事件处理回调函数来具体具体演示说明:
box1.addEventListener('chilk',function(){
console.log("我是外面的green的Box!");
}, true);
box2.addEventListener('chilk',function(){
console.log("我是外面的lightbule的Box!");
}, true);
body.addEventListener('chilk',function(){
console.log("body");
}, true);
document.addEventListener('chilk',function(){
console.log("document");
}, true);
window.addEventListener('chilk',function(){
console.log("window");
}, true);
- 可以看见当前事件捕获的顺序为:
window -> body -> green -> lightbule
由外到内的方式和冒泡的方式相反
事件传播的三个阶段
W3C综合了两个公司的方案,将事件传播分成了以下三个阶段:
- 事件捕获
- 目标阶段
- 冒泡阶段
捕获阶段
- 在捕获阶段时,从外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
目标阶段
- 事件捕获到目标元素,捕获结束开始在目标元素上触发
冒泡阶段
- 事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
通过DOM0
的写法和DOM2
的addEventListener
第三个参数为true
写法证明该阶段执行顺序:
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var body = document.querySelector("body");
/* ------冒泡阶段的代码-------- */
box1.onclick = function () {
console.log("冒泡:我是外面的green的Box!");
}
box2.onclick = function () {
console.log("冒泡:我是里面的lightbule的Box!");
}
body.onclick = function () {
console.log("冒泡:body");
}
document.onclick = function () {
console.log("冒泡:document");
}
window.onclick = function () {
console.log("冒泡:window");
}
/* ------捕获阶段的代码-------- */
box1.addEventListener('click',function(){
console.log("捕获:我是外面的green的Box!");
}, true);
box2.addEventListener('click',function(){
console.log("捕获:我是里面的lightbule的Box!");
}, true);
body.addEventListener('click',function(){
console.log("捕获:body");
}, true);
document.addEventListener('click',function(){
console.log("捕获:document");
}, true);
window.addEventListener('click',function(){
console.log("捕获:window");
}, true);
</script>
事件委托
- 事件委托指将绑定事件处理回调函数给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。
- 事件委托是利用了冒泡,通过事件委托可以减少事件处理函数绑定的次数,提高程序的性能
target || currentTarget
target
target
返回触发此事件的元素
也是事件委托的例子具体例子:
<ul id="box">
<li>江流</li>
<li>心猿</li>
<li>木龙</li>
<li>刀圭</li>
<li>意马</li>
</ul>
<script>
var ul = document.getElementById("box");
var lis = document.getElementsByTagName("li");
var li = Array.prototype.slice.call(lis);//slice()方法使lis返回一个数组
ul.onclick = function (event) {//给事件委托绑定事件处理回调函数
var e = event || window.event;//兼容IE8及以下的浏览器
var target = e.target || e.srcElement;//兼容火狐浏览器
console.log(li.indexOf(target)+"->"+target.innerText);//获取点击li元素的下标和文字
}
</script>
currentTarget
currentTarget
返回事件监听器触发该事件的元素
<ul id="box">
<li>江流</li>
<li>心猿</li>
<li>木龙</li>
<li>刀圭</li>
<li>意马</li>
</ul>
<script>
var ul = document.getElementById("box");
ul.onclick = function (event) {//事件委托
var e = event || window.event;
var currentTarget = e.currentTarget;
console.log(currentTarget);
}
</script>
取消事件的冒泡和捕获
- 一般的事件都有冒泡,但是
focus、onblur、scroll
没有冒泡
bubbles
bubbles
可以用来检查一个事件是否可以冒泡
box2.addEventListener('click', function (e) {
console.log(e.bubbles);
console.log("我是里面的lightbule的Box!");
}, false);
stopPropagation()
stopPropagation()
可以阻止事件的冒泡和捕获- IE8及以下不支持
<body id="body">
<div id="box1">
<div id="box2">点我!!!</div>
</div>
<script>
box1.addEventListener('click', function () {
console.log("我是外面的green的Box!");
}, false);
box2.addEventListener('click', function () {
console.log("我是里面的lightbule的Box!");
}, false);
body.addEventListener('click', function (e) {
var e = e || window.event;
e.stopPropagation();
console.log("body");
}, false);
document.addEventListener('click', function () {
console.log("document");
}, false);
window.addEventListener('click', function () {
console.log("window");
}, false);
</script>
</body>
stopImmediatePropagation()
-
stopImmediatePropagation()
方法阻止监听同一事件的其他事件监听器被调用。 -
如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。
-
如果在其中一个事件监听器中执行
stopImmediatePropagation()
,那么剩下的事件监听器都不会被调用。 -
也就是说在原本
stopPropagation()
基础之上,能够去阻止其他事件监听器被调用 -
IE8及以下不支持
cancelBubble
cancelBubble
可以阻止事件的冒泡和捕获- 用法:
e.cancelBubble = true
- 如果需要阻止其他事件监听器被调用则需要把函数单独命名然后设置为
null
<body id="body">
<div id="box1">
<div id="box2">点我!!!</div>
</div>
<script>
box2.addEventListener('click', function () {
console.log("我是里面的lightbule的Box!");
}, false);
box1.addEventListener('click', function (e) {
// e.stopPropagation();
console.log("我是外面的green的Box!");
}, false);
body.addEventListener('click', function (e) {
var e = e || window.event;
e.cancelBubble = true
console.log("body");
}, false);
document.addEventListener('click', function (e) {
var e = e || window.event;
e.stopPropagation();
console.log("document");
}, false);
window.addEventListener('click', function () {
console.log("window");
}, false);
</script>
</body>
兼容写法
function cancelBubble(e) {
var e = e || window.event;
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true;
}
}