什么是DOM
文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言(HTML 或者 XML )的标准编程接口
简言之,它会将web页面和脚本或程序语言连接起来。
html 和DOM的区别
- html就是标签,文档对象在页面中的表示,DOM就是对这些标签形成一个对象模型(DOM树),规定了这些标签对象的属性和方法
- 这些标签/元素在DOM下统称为节点
DOM事件流
事件流描述的是从页面中接收事件的顺序
事件发送时会在元素节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即DOM事件流
比如我们给一个div注册了点击事件:
捕获阶段:从根节点到事件触发的目标节点(根 –> 事件的目标元素)
document –> document.Element –> body –>
当前目标阶段:事件触发的目标元素(确定目标节点)
冒泡阶段:从触发事件的目标节点到根节点(事件的目标元素 –> 根)
事件捕获:
IE最早提出的,事件开始由最具体的元素接受,然后逐级向上传播到DOM最顶层节点的过程。
即:从外向内找监听函数
事件捕获:
网景公司最早提出的,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素的过程。
即:从内向外找监听函数
举个栗子:
我们向水里扔个石头,首先它会有个下降的过程、这个过程可以理解为捕获过程;之后会产生泡泡,然后漂流在水面上,这个过程相当于事件冒泡。
代码验证:
1 | <div class="father"> |
注意
- 目标元素(目标节点)鼠标去触发了事件的对象元素,不论该元素是否有该事件的对应处理函数
- 所有浏览器默认都为冒泡,ie只有冒泡,没有捕获
- 如果在一系列事件流中 既有捕获 又有冒泡 那么先捕获再冒泡
1 | div.addEventListener('click',()=>{ |
事件对象
1 | eventTarget.onclick = funciton(event){ |
这个 event 是个形参,系统会自动帮我们设定改为事件对象,不需要传递实参过去。
当我们注册事件时,event对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)
e.target 和 e.currentTarget 的区别
- e.target: 返回触发事件的对象。 即用户操作的对象。(假设:你点击了谁就是谁)
- e.currentTarget : 程序员监听的元素, 即你绑定了谁就是谁
- this就是e.currentTarget
打印一下就知道:

阻止冒泡
方法是 事件对象的
1 | function fn1(ev){ |
阻止默认事件
事件驱动时阻止默认事件 return false
事件绑定时阻止
addEventListener –> ev.preventDefault();
attachEvent –> ev.returnValue = false;
1
2
3
4
5
6
7
8
9//事件驱动时
oA.onclick = function(){
return false;
}
//事件绑定时
function fn1(ev){
ev.preventDefault? ev.preventDefault() : ev.returnValue = false
}
事件委托
原理:
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
举个场景:
比如给ul列表注册点击事件,然后利用事件对象的target来找到当前点击的li,因为点击了li,事件就会冒泡到ul身上,又因为ul有注册事件,就会触发事件监听器。
作用: 这样我们只操作了一次DOM, 省内存、可以监听动态元素
如何监听一个不存在的元素?
可以利用事件委托,监听父元素即可。
代码展示:
1 | <body> |
封装事件委托
要求:
写出一个函数 delegate('ul', 'click','li',fn) 当用户点击ul的li元素时,调用fn函数。要求用事件委托。
思路一
1 | function delegate(element, eventType, selector, fn) { |
看起来好像没什么问题,实际上是有bug的:
如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对
思路二
点击 span 后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。
1 | function delegate(element, eventType, selector, fn) { |