2002年,W3C发布了新的事件触发机制。
不过在这之前Netscape (网景)和 IE在这件事情互不相让。
网景认为,事件发生后是由外向内传播的。而IE跟网景是相反的,主张事件发生后是由内向外传播。
两家主张的事件标准如下(这里以为点击事件为例)
Node.attachEvent('onclick', fn) //IE的标准,也就是后来的冒泡
Node.addEventListener('click', fn) //网景的标准,也就是后来的捕获
那个时候的开发者也是非常头疼的,在ie下面要用ie的标准,在网景的浏览器则要用网景的标准。
于是W3C让两家和谈,最后在2002年发布了新标准。
W3C把网景和IE两家的标准整合成一起,规定浏览器应该同时支持两种调用顺序,它分为三个阶段。
- 第一阶段:从
window
对象传导到目标节点(上层传到底层),称为“捕获阶段”。 - 第二阶段:在目标节点上触发,称为“目标阶段”。
- 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”。
如上图,最底层的td
标签被点击了,就会先问Window
有没有函数要执行,如没有就会一直问下去直到td
标签。
这个过程走完之后,就来到了目标阶段,可以选择是否冒泡。如选择冒泡则从td
标签一直冒到Window
上。
W3C发布的新标准,规定了统一使用EventTarget.addEventListener()
给当前节点绑定一个特定事件的监听函数,一旦这个事件发生,就会执行监听函数。
语法
target.addEventListener(type, listener[, useCapture]);
参数
type
:事件名称,大小写敏感。listener
:监听函数。事件发生时,会调用该监听函数。useCapture
:布尔值,如果设为true
,表示监听函数将在捕获阶段(capture)触发。该参数可选,如不写则默认值为false
,监听函数会在冒泡阶段被触发。
function fn(){
console.log('hello')
}
div.addEventListener('click', fn)
上面的代码给div
的addEventListener
方法绑定了一个click
事件的监听函数fn()
。该函数在冒泡阶段触发。
优点EventTarget.addEventListener
是推荐的指定监听函数的方法。
它有如下优点:
捕获阶段是不可以取消的,只有冒泡阶段才能取消,stopPropagation方法
阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上的事件监听函数。
function fn(e){
e.stopProgation()
}
div.addEventListener('click', fn)
我们知道了监听函数的定义,但是如果遇到了一次性要给几十个,甚至几百个元素定义监听事件呢?
别慌,我们可以把监听事件定义到它们的父元素,由父元素的监听函数统一处理多个子元素的事件。
这种方法叫做事件委托。
let ul = document.querySelector('ul')
ul.addEventListener('click', (e)=>{
let t = e.target
if(t.tagName.toLowerCase() === 'li'){
console.log(t.textContent + '被点击了')
}
})
上面代码中,click
事件的监听函数定义在<ul>
节点,但是实际上,它处理的是子节点<li>
的click
事件。
优点
像上面事件委托的例子,只要定义一个监听函数,就能处理多个子节点的事件,而不用在每个
它还有一个好处,就是能监听目前不存在而以后会被添加的子节点,这个叫监听动态元素。
setTimeout(()=>{
let button = document.createElement('button')
button.textContent = 'click 1'
div.appendChild(button)
},1000)
div.addEventListener('click',(e)=>{
let t = e.target
if(t.tagName.toLowerCase() === 'button'){
console.log(t.textContent + '被点击了')
}
})
上面代码中,我们直接监听父元素div
的点击事件,这样它一添加子元素监听函数依然有效。
以上就是本次DOM事件模型的全部内容了,感谢观看。
本文参考:阮一峰. 网道:事件