1、DOM 事件
DOM体系相关知识点包括如下:
- DOM 事件流
- 事件对象
- 事件代理
- 自定义事件
2、事件的传播过程
W3C 标准约定了一个事件的传播过程要经过以下三个阶段:
- 事件捕获阶段
- 目标阶段
- 事件冒泡阶段

3、事件对象梳理
3.1 currentTarget
它记录了事件当下正在被哪个元素接收,即”正在经过哪个元素“。这个元素是一直在改变的,因为事件的传播毕竟是个层层穿梭的过程。
3.2 target
指触发事件的具体目标,也就是最具体的那个元素,是事件的真正来源。
3.3 preventDefault 阻止默认行为
这个方法用于阻止特定事件的默认行为,如 a 标签的跳转等。
e.preventDefault();
3.4 stopPropagation 不再派发事件
这个方法用于终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。
3.5 事件对象,是可以手动创建的
事件对象不一定需要你通过触发某个具体的事件来让它“自然发生”,它也可以手动创建的:
我们可以借助 Event() 构造函数, 来创建一个新的事件对象 Event。
event = new Event(typeArg, eventInit);
4.事件冒泡、事件代理
4.1 事件冒泡例子
<body><div id="div1"><p id="p1">激活</p><p id="p2">取消</p><p id="p3">取消</p><p id="p4">取消</p></div></body>
//通用事件绑定例子function bindEvent(elem,type,fn){elem.addEventListener(type,fn);}//事件冒泡const p1 = document.getElementById("p1")bindEvent(p1,'click',function(event){event.stopPropagation(); //阻止冒泡console.log('激活')})const body = document.body//事件冒泡 将事件绑定到body上实现取消bindEvent(body,'click',event=>{console.log('取消')})
4.2 使用事件代理场景
所谓事件代理,因为数量太多不好去每个绑定事件,运用事件冒泡的特征把它绑定到父元素。
- 要你安装监听某一个事件的监听函数(事件相同)
- 监听函数是被安装在一系列具有相同特征的元素上(元素特征相同,一般来说就是具备同样的父元素)
- 这一系列相同特征元素上的监听函数还干的都是一样的事儿(监听逻辑相同/雷同)
4.3 事件代理例子
<div id="div3"><a href="#">a1</a><a href="#">a2</a><a href="#">a3</a><a href="#">a4</a></div><button>点击增加一个a标签</button>
//事件代理const div3 = document.getElementById("div3")bindEvent(div3,'click','a',function(event){event.preventDefault();alert(this.innerHTML)})function bindEvent(elem,type,selector,fn){if(fn == null){fn = selectorselector = null}elem.addEventListener(type,event=>{const target = event.targetif(selector){//代理绑定if(target.matches(selector)){fn.call(target,event)}}else{fn.call(target,event)}})}
4.4 事件代理特点
- 事件代理代码简洁
- 减少浏览器内存占用
- 但是不要滥用
5、自定义事件
有这样一个场景:在点击A之后,B 和 C 都能感知到 A 被点击了,并且做出相应的行为——就像这个点击事件是点在 B 和 C 上一样。在这样的场景下就诞生了自定义事件。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>Document</title><style>#divA,#divB,#divC {width: 200px;height: 200px;color: #fff;}#divA {background-color: red;}#divB {background-color: black;}#divC {background-color: blue;}</style></head><body><div id="divA">我是A</div><div id="divB">我是B</div><div id="divC">我是C</div><script>var clickAEvent = new Event("clickA");// 获取 divB 元素var divB = document.getElementById("divB");// divB 监听 clickA 事件divB.addEventListener("clickA", function (e) {console.log("我是B,我感觉到了A");console.log(e.target);});// 获取 divC 元素var divC = document.getElementById("divC");// divC 监听 clickA 事件divC.addEventListener("clickA", function (e) {console.log("我是C,我感觉到了A");console.log(e.target);});divA.addEventListener("click", function () {console.log("我是A");// 注意这里 dispatch 这个动作,就是我们自己派发事件了divB.dispatchEvent(clickAEvent);divC.dispatchEvent(clickAEvent);});</script></body></html>

6、典型题
6.1 编写一个通用的事件监听函数
function bindEvent(elem,type,selector,fn){if(fn == null){fn = selectorselector = null}elem.addEventListener(type,event=>{const target = event.targetif(selector){//代理绑定if(target.matches(selector)){fn.call(target,event)}}else{fn.call(target,event)}})}
6.2 事件冒泡的流程
- 基于DOM树形结构
- 事件会顺着触发元素向上冒泡
- 应用场景:代理
6.3 无限下拉的图片列表,如何监听每个图片的点击
- 事件代理
- 用e.target获取触发元素
- 用matches来判断是否是触发元素
