事件模型
事件模型分为事件捕获事件冒泡两种机制。而事件委托其实是得益于事件冒泡衍生出来的一种事件监听方式
下图展示了它们三者的关系
事件捕获和冒泡的关系
事件捕获和事件冒泡是浏览器在执行事件时的不同阶段, 最初的事件机制只有微软使用的事件冒泡机制,当你点击一个元素后事件从你点击的元素沿着DOM树向高层根节点冒泡终点是window对象,当遇到存在有对应监听程序的DOM节点时就会触发执行事件处理函数。
捕获是当时网景公司专门针对微软而实现的相反的事件机制,捕获是由起点window对象开始沿DOM树从外向内进行捕获直到找到绑定事件的元素,同样是遇到监听过事件处理程序的DOM元素时就执行事件处理函数。
最终W3C为了统一标准将事件捕获和事件冒泡都纳入了事件模型。
理解捕获和冒泡
就像我们用手深入水中抓鱼这里的鱼就代表事件的目标对象,这就是捕获阶段,完成抓捕之后产生的气泡往水面上浮就是冒泡阶段。
点击查看事件机制
事件冒泡
事件冒泡理解为在捕获到指定的DOM节点之后由该DOM节点向外沿每层的父节点冒泡。其中经过的所有父节点都会查看是否有对应的事件绑定程序。当发现绑定过事件处理程序就会执行响应的代码,还可以通过传入的event对象查看是由那个元素发起的事件。通过event.target就可以确定是那个元素发起的事件,因此在父元素上对其进行处理事件也变成了可能(我们把这种事件处理方式称为事件委托)。
设置事件冒泡
直接使用target.addEventListener(event,fnc)函数的默认值,或显示的设置最后一个参数为falsetarget.addEventListener(event,fnc,false)
取消事件冒泡
使用事件处理对象e.stopPropagation()方法阻止事件的传播,阻止了事件向外传播之后就不会再触发所有父元素上的事件处理程序。也可以在父结点上阻止事件冒泡。
事件捕获
事件捕获可以理解为从最顶成的根节点开始向内捕捉指定的DOM节点,其中经过的所有父节点都会查看是否有对应的事件绑定程序。
设置事件捕获
使用target.addEventListener(event,fnc,true)最后一个参数来设置捕获还是冒泡,默认为事件冒泡,为true时事件捕获。
阻止事件捕获
e.stopPropagation一样适用与阻止事件捕获,阻止了事件捕获后事件捕获和冒泡机制也会被阻止。比如在某父元素上定义阻止了事件捕获那么其子元素对应的事件处理程序就无法继续往下捕获。不妨可以在控制台中输入以下这段代码那么页面中所有的click事件都会被阻止
window.addEventListener('click',function(e){
e.stopPropagation();
},true)
如果目标对象(指的就是点击的e.target)事件冒泡和事件捕获都监听了那么它的执行顺序是按照代码的监听顺序的,并不是先捕获再冒泡
事件委托
事件委托是事件冒泡的最佳实践,将子元素的事件委托给父元素或祖先元素处理,所使用的就是冒泡机制。下面代码封装了一个事件委托程序
- 所谓的事件代理是指把元素事件绑定代理给某个父级元素,由该父级元素根据事件来源统一处理
- 适用于可能会新增子元素的场景
- 事件代理其实是事件冒泡的一种应用
function on(parent, event, selector, fn) {
parent.addEventListener(event, function(e) {
element = e.target;
//当前操作的元素如果不是所委托的元素就向上层寻找,直到找到或等于parent
while (!(element.matches(selector))) {
if (parent === element) {
element = null;
break;
}
element = element.parentNode;
}
element && fn(e)
})
}
事件委托就是把事件监听放在祖先元素(如父元素、爷爷元素)上。
好处是:
1.节约监听数量 2.可以监听动态生成的元素。
总结
DOM事件
捕获:当用户点击按钮,浏览器会从 window 从上向下遍历至用户点击的按钮,逐个触发事件处理函数。
冒泡:浏览器从用户点击的按钮从下往上遍历至 window,逐个触发事件处理函数。
W3C 事件模型/事件机制:对每个事件先捕获再冒泡
- e.stopPropagation()可以用来阻止事件传播,捕获和冒泡都适用
- 事件委托就是通过事件冒泡机制实现的
- 事件捕获和事件冒泡的操作元素为同一个时是看代码的先后顺序来执行的,并不是先捕获再冒泡
- e.preventDefault()阻止默认事件的动作