DOM事件机制是什么?
简单来说DOM事件机制就是为了解决下面这类问题
<div class = "grandfather">
<div class = "father">
<div class="son">
文字内容
</div>
</div>
</div>
在上面的HTML代码中,我们分别给三个divt添加上点击事件监听fnG,fnF,fnS。那么问题来了,
1.点击文字,,点击了谁?算不算点击son? 算不算点击father?算不算点击grandfather?
答案:都算
2.点击文字,调用顺序?先调用fnG/fnF/fnS中哪个函数监听?
答案:都行,当时IE5认为应该先调用fnS,而网景浏览器则认为应该先调用fnG。
2002年,w3c制定标准,文档名为DOM Level2 Event Specification,认为浏览器应该同时支持两种调用顺序。
首先按照fnG-->fnF-->fnS的顺序来检查监听
然后按照fnS-->fnF-->fnG的顺序来检查监听
有监听函数就执行,并提供事件信息,没有就跳过。
专业术语就是:
从外向内找监听,就叫事件捕获。
从内向外找监听,就叫事件冒泡。
以上就是我们所称的DOM事件机制。
值得注意的是,这些函数并不是调用两次,而是开发者自己可以选择在哪个阶段执行函数监听,即在添加事件监听时通过不同传参来确定。
下面放一张示图来帮助理解事件捕获和事件冒泡。
添加事件监听的具体API: addEVentListener
father.addEventListener('onclick',fn) //IE5,冒泡
father.addEventListener('click',fn) //网景,捕获
father.addEventListener('click',fn,bool)
//w3c,如果bool不传或者为falsy值,让fn在冒泡阶段执行;
//如果bool为true则让fn在捕获阶段执行
如上API,开发者可以自己决定将监听函数fn放在事件捕获阶段还是事件冒泡阶段,从而实现不同用户需求。
事件信息event和target,currentTarget
father.addEventListener('click',(e)=>{
const t = e.currentTarget();
settimeout(()=>{
t.classList.add('x')
},1000)
});
上面这一块代码是给class为father的div添加监听的具体实现。函数大概就是1秒后给当前事件目标加一个x的类名。当我们添加监听事件时,浏览器会返回我们整个事件信息event,这里就是e这个变量,它携带的就是当前事件信息。
e.target(); 是指用户操作的元素
e.currentTarget();是得到开发者当前监听的元素
举例说明:div> span{文字};当监听div而点击文字时,e.target是文字,e.currentTarget是div。
注意点:在这里的this,是e.currentTarget();
特例:
1)当只有一个div被监听时(不考虑父子同时被监听)
e.target和e.currentarget是同一个对象。
2)与此同时,当div分别在捕获阶段和冒泡阶段设置监听函数fn,执行顺序又是如何?
执行顺序按照设置顺序来定,也就是说哪个监听函数写在前面,哪个就先执行。
e的生命周期
e在每次监听函数执行之后会发生变化,携带当前事件信息已经不再准确,比如currentTarget会被清掉。所以不应该将其作为标识变量来使用。可以在监听函数里用一个变量保存当前值来使用。
取消冒泡
DOM事件中,捕获不可取消,但是冒泡可以。
e.stopPropagation()可以中断冒泡,浏览器不再向上传递事件。
一般用于封装某些独立的控件。
这里也有特例:有些事件不能阻止默认动作。
比如scroll event,查看MDN文档,我们可以发现它有这两个属性Bubbles 和Cancelable;
Bubbles表示该事件是否冒泡;Cancellable表示是否支持开发者取消冒泡;而scroll事件就不支持取消冒泡。
自定义事件
浏览器自带100多个事件,在MDN文档即可查看。
于此同时,开发者还可以自己自定义事件。代码示例如下,实现点击sonDIV事执行alice事件函数,函数内容打印出‘alice’。
let sonDIV = document.querySelector('.son');
//创建一个事件对象
const eventNew = new CustomEvent('alice',{'detail':{'name':'amiee','age':18}});
//将点击事件分发到新建的事件eventNew中去
sonDIV.addEventListener('click',()=>{
sonDIV.dispatchEvent(eventNew);});
//设置eventNew事件监听,并实现函数内容
sonDIV.addEventListener('alice',(e)=>{
console.log('alice')
})
事件委托
事件委托,又称事件代理。顾名思义,“事件委托”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父级元素,让其担当事件监听的职务。具体操作就是将监听事件及函数加在父级元素上面。
需求场景,举例说明:
1.当需要给100个按钮加点击事件,如何处理更方便?
答:监听其祖先元素,当其冒泡时,我们再通过判断target来做相应处理。
2.当页面是动态生成一些按钮,但是你需求提前对它们做好监听,如何处理?
答:监听其祖先元素,在监听事件里面再判断是否是我需要监听的元素。
由此可以看出事件委托的优点:1)省监听数(即省内存) 2)可以动态监听