1 DOM 事件模型(DOM 事件机制)
HTML DOM 允许 JavaScript 对 HTML 事件作出反应。JavaScript 能够在事件发生时执行,比如当用户点击某个 HTML 元素时。JavaScript与HTML之间的交互是通过事件实现的。
事件由事件源、事件类型和事件处理程序三部分组成
事件执行步骤:获取事件源 => 注册(绑定)事件 => 采用函数赋值形式添加事件处理程序
2 事件流
事件流描述了页面接受事件的顺序,IE 和 Netscape 开发团队提出了几乎完全相反的事件流方案,IE支持事件冒泡流,Netscape 支持事件捕获流。2002年,W3C发布标准,规定浏览器应该支持两种调用方式,开发者自己选择把函数放在捕获阶段还是冒泡阶段。
2.1 事件捕获
Netscape事件流被称作事件捕获流,规定为从外向内找监听函数的过程。
过程实例:当点击div元素时,触发click事件顺序:
document => html => body => div
在事件捕获中,click事件首先由document元素捕获,随后沿着DOM树向下传播,直到到达目标元素。
由于旧版本浏览器不支持,因此通常建议使用事件冒泡,特殊情况使用时间捕获。
2.2 事件冒泡
IE事件流被称作事件冒泡流,规定为从内向外找监听函数的过程。
过程实例:当点击div元素时,触发click事件顺序:
div => body => html => document
在事件冒泡中,被点击的元素div最先触发click事件,然后,click事件沿着DOM树一路向上,在经过的每个节点上以此触发,直至到达document对象
所有现代浏览器都支持事件冒泡,但实现方式存在部分变化,如IE5.5早期版本会跳过html元素,直接从body到document,现代浏览器中的事件会一直冒到window
取消冒泡(阻止事件冒泡)
- 捕获不可以取消,但冒泡可以
-
e.stopPropagation()
可以中断(取消)冒泡,浏览器不在向上走 - 一般用于封装某些独立组件
不可阻止默认动作
- 阻止默认动作语法:
e.preventDefault()
- 所有冒泡皆可取消,但默认动作有的可以取消有的不能取消
- MDN搜索scroll event,看到 Bubbles(该事件是否冒泡) 和 Cancelable(开发者是否可以阻止默认事件)
-
取消scroll滚动事件:
不可取消默认动作,因为现有滚动才有滚动事件
但可以阻止wheel和touchstart的默认动作,注意需要找准滚动条所在元素,同时用CSS让滚动条width:0;
也可使用overflow:hidden;
直接取消滚动条,但此时JS仍然可以修改scrollTop
2.3 addEventListener()
W3C模型
- 先捕获(父 => 子),再冒泡(子 => 父)
- 特例:当只有一个div被监听(不考虑父子同时被监听),fn分别在捕获阶段和冒泡阶段监听click事件,用户点击的元素就是开发者监听的,则谁先监听谁先执行(2022年已修复,变为先捕获再冒泡)
- 注意e对象会被传给所有监听函数
- 事件结束后,e对象就不存在了(其实是存在的,只是变成了currentTarget = null,更多了解后面再讨论,暂时认为不存在了),如果需要继续使用,可以使用变量保存
const t = e.currentTarget
注意:关于target
和currentTarget
的区别:
- e.target—用户操作的元素
- e.currentTarget—程序员监听的元素
- this就是e.currentTarget
- 实例:div > span{文字},用户点击文字时,e.target就是span,e.currentTarget是div
事件绑定API
- IE5*:
// 冒泡
father.attachEvent('onclick', fn)
- 网景(Netscape ):
// 捕获
father.addEventListener('click', fn)
- W3C:
// bool处为布尔值,决定采用冒泡还是捕获,
// 不填时默认为false,则使用冒泡
// 填true时为网景的捕获
father.addEventListener('click', fn,bool)
3 事件委托
事件委托就是利用冒泡的原理,但事件触发时,把要做的事件委托给父元素(或父元素的父元素)来处理,将事件加到父级上,通过判断事件来源的子集,执行相应操作,从而避免对特定每个节点添加事件监听器,减少占用内存空间,提升性能。
事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。
实例:
情景:需要给100个按钮添加点击事件。
解决方法:监听这100个按钮的祖先,由于这100个按钮都是祖先的后代,所以他们的事件都会向上冒泡,最终都会由添加给祖先的函数来处理,只需要检查具体子元素(event对象)的相应属性/内容就可以确定,然后执行相应操作即可。需要监听目前不存在的元素的点击事件时,也可以通过事件委托实现。
解决方法:监听目标元素的祖先,等点击的时候再查看是否是需要的监听的元素即可
优点
- 减少页面所需内存,提升整体性能
- 可以用于监听动态元素
- 节省花在设置页面事件处理程序上的时间(只用指定一个处理函数,节省DOM引用)
- document 对象随时可用,任何时候都可以给它添加事件处理程序,只要页面渲染出可点击的元素,就可以无延迟地起作用
适用事件
最适合使用事件委托的事件包括:click
、mousedown
、mouseup
、keydown
和 keypress
主要事项
- JS支持,也不支持事件,DOM事件不属于JS的功能,属于浏览器提供的DOM的功能
- JS只是调用了DOM提供的addEventListener而已