dom事件流

事件捕获(event capturing)

当使用事件捕获时

               | |
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

outer上的事件处理器先触发, 然后是inner上的

事件冒泡(event bubbling)

               / \
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

与事件捕获相反, 当使用事件冒泡时, inner上的事件处理器先被触发, 其后是outer上面的。

一般来说事件冒泡机制,用的更多一些,所以在IE8以及之前,IE只支持事件冒泡。IE9+/FF/Chrome这2种模型都支持,可以通过addEventListener((type, listener, useCapture)的useCapture来设定,useCapture=false代表着事件冒泡,useCapture=true代表着采用事件捕获。默认是false,即事件冒泡。

DOM事件流

DOM事件流:将事件分为三个阶段:捕获阶段、目标阶段、冒泡阶段。先调用捕获阶段的处理函数,其次调用目标阶段的处理函数,最后调用冒泡阶段的处理函数。

我们在outC上触发onclick事件(这个是目标对象),如果我们在outC上同时绑定捕获阶段/冒泡阶段事件处理函数会怎么样呢?

<script>

 window.onload = function(){
 var outA = document.getElementById("outA"); 
 var outB = document.getElementById("outB"); 
 var outC = document.getElementById("outC"); 
 
 // 目标(自身触发事件,是冒泡还是捕获无所谓)
 outC.addEventListener('click',function(){alert("target2");},true);
 outC.addEventListener('click',function(){alert("target1");},true);
 
 // 事件冒泡
 outA.addEventListener('click',function(){alert("bubble1");},false);
 outB.addEventListener('click',function(){alert("bubble2");},false);
 
 // 事件捕获
 outA.addEventListener('click',function(){alert("capture1");},true);
 outB.addEventListener('click',function(){alert("capture2");},true);

 
 
 };
 
</script>

<body>
 <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;">
 <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">
  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
 </div>
 </div>
</body>

点击outC的时候,打印顺序是:capture1-->capture2-->target2-->target1-->bubble2-->bubble1。由于outC是我们触发事件的目标对象,在outC上注册的事件处理函数,属于DOM事件流中的目标阶段。目标阶段函数的执行顺序:先注册的先执行,后注册的后执行。这就是上面我们说的,在目标对象上绑定的函数是采用捕获,还是采用冒泡,都没有什么关系,因为冒泡和捕获只是对父元素上的函数执行顺序有影响,对自己没有什么影响。如果不信,可以将下面的代码放进去验证。

// 目标(自身触发事件,是冒泡还是捕获无所谓)
outC.addEventListener('click',function(){alert("target1");},false);
outC.addEventListener('click',function(){alert("target2");},true);
outC.addEventListener('click',function(){alert("target3");},true);
outC.addEventListener('click',function(){alert("target4");},false);

至此我们可以给出事件函数执行顺序的结论了:捕获阶段的处理函数最先执行,其次是目标阶段的处理函数,最后是冒泡阶段的处理函数。目标阶段的处理函数,先注册的先执行,后注册的后执行。

阻止默认事件

1. 阻止冒泡事件

主要是用于阻止事件传播。阻止它被分派到其他的DOM节点上,在事件传播的任何阶段都能使用。使用方法如下(兼容IE):

function stopBubble(event){
	if(window.event){//兼容IE
		window.event.cancelBubble=true;
	}else{
		event.stopPropagation();
	}
 

2. 阻止默认事件

像submit这类的表单元素,都会绑定默认事件,如果不阻止默认事件,则绑定的其他方法也会无效。使用方法如下(兼容IE):

function stopDefaultEvent(event){
	if(window.event){//兼容IE
		window.event.returnValue=false;
	}else{
		event.preventDefault()
	}
	return false;
}

 

事件委托、时间代理

 

上面的例子是要阻止冒泡,有时候冒泡机制也可以被利用。先看一个问题:

假设要在页面上放在 10^n(n>=1) 个列表项元素,当我点击某个元素时,需要输出点击的是第几个。

一般做法是,遍历时给每个元素绑定点击事件:

var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
    li[i].setAttribute('i',i+1);
    li[i].addEventListener('click', function(e){
        var b = this.getAttribute('i');
        console.log('这是第' + b + '个<li>元素');
    });
}

另一种方法是,可以利用冒泡机制,在父元素只绑定一次点击事件:

var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
    li[i].setAttribute('i',i+1);
}

var ul = document.getElementById('wrapper');
ul.addEventListener('click', function(e){
    if(e.target && e.target.nodeName.toUpperCase() === 'LI'){
        var b = e.target.getAttribute('i');
        console.log('这是第' + b + '个<li>元素');
    }
});

这种事件委托的方式减少了事件处理程序,也能降低程序的复杂性和出错概率。

参考:

https://www.jb51.net/article/94394.htm

https://blog.csdn.net/michael8512/article/details/77447537

posted @ 2018-08-13 14:19  ziqingmo  阅读(203)  评论(0编辑  收藏  举报