在早期的浏览器厂商都认为页面中的元素事件都不仅仅是当前元素上,而是相关的其他元素甚至整个页面都应该相关的机制。但有意思的是,早期的两个开发团队分别是ie和Netscape却提出了完全相反的事件流概念。也就是下面要介绍的两种。
场景:在很多时候我们会遇到类似的问题,我最早遇到的场景是在a标签上定义了连接地址,然后a标签内定义的子标签定义的其他点击事件会在执行前就先页面跳转了。 常识:作为常识我们要知道事件函数中的this指向的是被点击的目标元素,稍后的讲解中会区分目标元素以及托管的元素。
ie提出的是冒泡,也就是从最具体的触发元素对象一直向上传递。
graph LR
childElement(span)-->parentNode(div)
parentNode(div)--> parentNode2(body)
let button = document.getElementById("button")
button.addEventListener('click',function(e){
//come code
//避免冒泡
e.preventDefault()
})
捕获与冒泡的机制相反,是指元素会捕获所有子元素的事件,进而检查自己是否具有事件绑定,从而触发。
这个就不画流程图了,写下事件的定义好了,如果利用捕获机制,定义事件会写成下面这样。
let ul = container.getElementById("container")
container.addEventListener("click",funtion(e){
// 不考虑兼容写法
let target = e.target
//监听到点击后,查看具体目标是什么,具体处理
switch(target){
case 'span' : some code
}
})
dom2级的事件流规定:事件流包括三个阶段:事件捕获阶段、目标阶段、冒泡阶段。
在多数的浏览器里规定了事件不会在捕获阶段触发事件,只会在目标阶段触发,而后才会触发冒泡阶段。但有的浏览器在捕获阶段也会触发事件,于是导致了两次父元素的事件触发。
待进一步验证谷歌的事件机制是怎样的 。。。
这一类在我们原生开发的时候很常见,直接用标签的事件属性绑定函数,在vue的框架写法中也是类似的写法,至于其底层是html事件处理还是下面的dom0级,后面我会在查看对应的源码,给大家分析下vue的@click 底层是是如何处理事件的。
将一个函数赋值给一个事件处理属性,这种方式跨浏览器,写法简单,兼容性好,但是它需要一个对元素的引用,所以如果这个事件如果定义在元素的前面,那么事件就不会绑定上。
其中,处理函数中的this指的是当前元素对象。
let btn = document.getElementById('btn');
btn.onclick = function(){
console.log(this)
}
dom2级事件定义了两个方法,用于处理指定程序和删除事件处理程序的操作。所有的dom节点都包含这样的方法,这个方法支持三个参数,要处理的事件名,处理事件的函数,和一个布尔值。如果为true,代表捕获阶段处理,如果为false,代表冒泡阶段调用函数。
let btn = document.getElementById('btn');
btn.addEventListener("click", function(){
console.log(this)
},false)
btn.addEventListener("click", function(){
console.log('hello world')
},false)
dom2级与前面方式的区别在于可以为一个事件绑定多个函数而不会是替换关系,这个更符合我们的实际需求。
通过addEventListener添加的函数只能通过removeEnentListener来移除,这意味着如果你添加的是匿名函数,是无法移除的,因为匿名函数没有对应的名称和指引找到,在移除具名函数的时候,只要第二个参数写具名函数的名称即可。
attachEvent与detatchEvent 分别可以用来为ie增加和移除事件监听程序。与dom0级不同的是,其是为全局添加的,也就是事件里的this为window。
与dom2级的监听程序一样,也可以为同一个事件增加多个监听,但其没有配置项,默认的是在捕获阶段捕获事件的。
需要注意的时候事件的执行顺序不是按照添加的顺序,而是按照相反的顺序执行的。
这里我们稍微带一下设计模式里的外观模式,外观模式就是让用户不关心具体实现和方案分配,只关心效果的设计模式。
也可以理解为通过一个暴露接口可以调用底层的api的一个设计。
那么我们浏览器的兼容写法其实也是外观模式的设计思路。
基于以上的常识,我们用外观模式设计的监听程序应该是这样写的:
var EventUtil = {
addHandler:function(ele,type,handler){
if(ele.addEventListener){
ele.addEventListener(type,handler,false)
} else if(ele.attactEvent){
ele.attactEvent(type,handler)
} else{
ele['on'+type] = handler
}
},
removeHandler:function(ele,type,handler){
//codes here
}
}