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 = selector
selector = null
}
elem.addEventListener(type,event=>{
const target = event.target
if(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 = selector
selector = null
}
elem.addEventListener(type,event=>{
const target = event.target
if(selector){
//代理绑定
if(target.matches(selector)){
fn.call(target,event)
}
}else{
fn.call(target,event)
}
})
}
6.2 事件冒泡的流程
- 基于DOM树形结构
- 事件会顺着触发元素向上冒泡
- 应用场景:代理
6.3 无限下拉的图片列表,如何监听每个图片的点击
- 事件代理
- 用e.target获取触发元素
- 用matches来判断是否是触发元素