DOM事件机制简述(上)

755 阅读4分钟

DOM 事件参考

发送DOM事件是为了将发生的相关事情通知代码。每个事件都是继承自Event 接口的对象,可以包括自定义的成员属性及函数用于获取事件发生时相关的更多信息。事件可以表示从基本的用户交互、到发生在渲染模型自动通知的任何事情。


DOM 事件机制

从一个代码示例入手

HTML代码块:

<div id="grandfather">
    <div id="father">
        <div id="child">
            文字
        </div>
    </div>
</div>

即有.grandfather>.father>.child
给这3个div分别添加事件监听 fngfa / fnfa / fnchd

提问1:点击文字,算不算点击了childfathergrandfather
答:都算。
提问2:点击文字,最先调用 fngfa / fnfa / fnchd 中的哪个函数?
答:都行。

因此,为了解决页面中的事件流(事件发生顺序)的问题。微软公司和网景公司在当时分别提出了事件冒泡和事件捕获两个概念。
事件冒泡(event bubbling),即事件会从最内层元素开始发生,一直向上传播,直到document对象。
事件捕获(event capturing),即事件会从最外层开始发生,一直向下传播,直到最具体的元素。
2002年,W3C发布标准。文档名为:DOM level 2 Events Specification。其规定浏览器应该同时支持两种顺序。而且,首先按事件捕获调用顺序,然后按事件冒泡调用顺序。

事件流调用示意图

flowofevnets.png


疑问:那岂不是 fngfa / fnfa / fncld 都调用了两次?
答:并不会,开发者一开始就需要自己选择把调用函数放在捕获阶段还是冒泡阶段。

事件绑定API

添加事件监听函数/API
IE 5*: child.attatchEvent('onclick', fn) // 冒泡
网景: child.addEventListener('click', fn) // 捕获
W3C: child.addEventListener('click', fn, bool)

如果bool不传或为falsy
就让fn走冒泡阶段,即当浏览器在冒泡阶段发现childfn监听函数,就会调用fn,并提供事件信息。

如果bool为ture
就让fn走捕获阶段,即当浏览器在捕获阶段发现childfn监听函数,就会调用fn,并提供事件信息。

开发者选择fn所在阶段示意图

eventbool.png

点击:捕获和冒泡示例代码

点击:捕获和冒泡演示

代码图解 codediagram.png


事件其他操作

targetcurrentTarget
e.target 是用户操作的元素
e.currentTarget 是程序员监听的元素
thise.currentTarget (不推荐使用,上面一眼区分)

举例
div>span{文字},用户点击文字
e.target 就是 span
e.currentTarget 就是 div

一个特例
当只有一个div被监听 (不考虑父子同时被监听), fn 分别在捕获阶段和冒泡阶段监听 click 事件 用户点击的元素就是开发者监听的

举例代码

div.addEventListener('click', f1)
div.addEventListener('click', f2, true)

请问,f1先执行还是f2先执行?
如果把两行调换位置后,请问先执行哪个?
错误答案:f2先执行。
正确答案:谁先监听谁先执行。
总结:这是一个特例。


取消冒泡

e.stopPropagation() 可中断冒泡,浏览器不再向上走
一般用于封装某些独立的组件。

不可阻止默认动作

有些事情不能阻止默认动作
MDN 搜索 scroll event,能看到Bubbles 和 Cancelable
Bubbles 是指该事件是否冒泡,所有的冒泡都可以取消
Cancelable 是指开发者是否可以阻止默认动作。其与冒泡无关。
搜索时,推荐看英文版,中文版本内容不全,如下图示:

fres.png

插曲:如何阻止滚动

scroll事件不可以阻止默认动作:
阻止scroll默认动作没有用,是因为先有滚动才有滚动事件。
可阻止wheeltouchstart的默认动作,来阻止滚动
但需要注意找准滚动条所在的元素。

另外,在CSS使用:

overflow:hidden

::-webki-scrollbar {
    width: 0 !important
}

可以直接取消滚动条。但此时JS依然可以修改scrollTop


自定义事件

浏览器自带的事件一共有100多种,在MDN事件列表可以查看

提问:开发者能不能在自带事件之外,自定义一个事件? 答:可以。

button1.addEventListener('click',()=>{
    const event = new CustomEvent("newattr",
    {
    "detial":{name:'myname',age: 18}
    })
    button1.dispatchEvent(event)
})

button元素添加了一个点击触发newattr事件
开发者工具打印出:

newevent.png


参考资料

MDN Event reference

©转载声明