DOM是用来呈现以及与任意 HTML 或 XML 文档交互的 API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分
DOM事件流有3个阶段:捕获阶段,目标阶段,冒泡阶段;
三个阶段的顺序为:捕获阶段——目标阶段——冒泡阶段;
原因:因为Window对象是直接面向用户的,那么用户触发一个事件,如点击事件,肯定是用window对象开始的,所以自然就是先捕获后冒泡。
不管对于非目标阶段或者目标阶段的元素,事件响应执行顺序都是遵循先捕获后冒泡的原则;通过使用定时器暂缓执行捕获事件,可以达到先冒泡后捕获的效果;
事件捕获是从顶层的Window逐层向内执行,事件冒泡则相反;
事件委托(事件代理)是根据事件冒泡或事件捕获的机制来实现的。
DOM事件流
事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
在DOM事件流中,事件的目标在捕获阶段不会接受到事件。这意味着在捕获阶段,事件从document到p后就定停止了。
下一个阶段是处于目标阶段,于是事件在p上发生,并在事件处理中被看成冒泡阶段的一部分。然后,冒泡阶段发生,事件又传播回document。
多数支持DOM事件流的浏览器都实现了一种特定的行为;即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上操作事件
addEventListener函数
**MDN定义:**addEventListener() 的工作原理是将实现 EventListener 的函数或对象添加到调用它的 EventTarget 上的指定事件类型的事件侦听器列表中。如果要绑定的函数或对象已经被添加到列表中,该函数或对象不会被再次添加。
addEventListener(type, listener);
addEventListener(type, listener,options);
addEventListener(type, listener, useCapture);
参数
type:表示监听事件类型的大小写敏感的字符串。listener
当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。
listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数。
useCapture
默认值false,表示事件冒泡;设为true时,表示事件捕获
事件冒泡举例
<div id="1" style="width: 100%; height: 300px;background-color: red;">1<div id="2" style="width: 100%; height: 200px;background-color: blue;">2<div id="3" style="width: 100%; height: 100px;background-color: skyblue;">3</div></div>
</div>
<script>var a = document.getElementById('1')var b = document.getElementById('2')var c = document.getElementById('3')//注册冒泡事件监听器a.addEventListener('click', () => {console.log("冒泡1")})b.addEventListener('click', () => {console.log('冒泡2')})c.addEventListener('click', () => {console.log("冒泡3")})
</script>
冒泡事件的执行顺序为:c -> b -> a
事件捕获举例
<div id="1" style="width: 100%; height: 300px;background-color: red;">1<div id="2" style="width: 100%; height: 200px;background-color: blue;">2<div id="3" style="width: 100%; height: 100px;background-color: skyblue;">3</div></div>
</div>
<script>var a = document.getElementById('1')var b = document.getElementById('2')var c = document.getElementById('3')//注册冒泡事件监听器a.addEventListener('click', () => {console.log("捕获a")}, true)b.addEventListener('click', () => {console.log('捕获b')}, true)c.addEventListener('click', () => {console.log("捕获c")}, true)
</script>
捕获事件的执行顺序为:a -> b -> c
冒泡事件和捕获事件的区别
<div id="1" style="width: 100%; height: 300px;background-color: red;">1<div id="2" style="width: 100%; height: 200px;background-color: blue;">2<div id="3" style="width: 100%; height: 100px;background-color: skyblue;">3</div></div>
</div>
<script>var a = document.getElementById('a')var b = document.getElementById('b')var c = document.getElementById('c')a.addEventListener('click', () => {console.log("冒泡a")})b.addEventListener('click', () => {console.log('冒泡b')})c.addEventListener('click', () => {console.log("冒泡c")})a.addEventListener('click', () => {console.log("捕获a")}, true)b.addEventListener('click', () => {console.log('捕获b')}, true)c.addEventListener('click', () => {console.log("捕获c")}, true)
</script>
事件代理(事件委托)
**事件代理就是利用事件冒泡或事件捕获的机制把一系列的内层元素事件绑定到外层元素。
- itema
- itemb
- itemc
- itemd
对于上述的列表元素,我们希望将用户点击了哪个item打印出来,通常我们可以给每个item注册点击事件监听器,但是需要对每个元素进行事件监听器的注册;但是通过事件代理,我们可以将多个事件监听器减少为一个,这样就减少代码的重复编写了。利用事件冒泡或事件捕获实现事件代理:
javascript复制代码var items = document.getElementById(‘item-list’);
//事件捕获实现事件代理
items.addEventListener(‘click’, (e) => {console.log('捕获:click ',e.target.innerHTML)}, true);
//事件冒泡实现事件代理
items.addEventListener(‘click’, (e) => {console.log('冒泡:click ',e.target.innerHTML)}, false);
说到这里,大家都明白了,事件代理既可以通过事件冒泡来实现,也可以通过事件捕获来实现