一、DOM事件
事件是用户或者浏览器自己执行的某种动作,是文档或者浏览器发生的一些交互瞬间,比如点击(click)按钮等,这里的click就是事件的名称。JS与html之间的交互是通过事件实现的,DOM支持大量的事件。
事件并不是只是在一处被触发和终止;他们在整个document中流动,拥有它们自己的生命周期。
二、DOM事件流
事件流又称为事件传播,描述的是从页面中接收事件的顺序。如下图所示,DOM 是个树形结构,我们在页面上点击一个元素,事件对象将通过 DOM 事件流确定的 DOM 树进行传播。
事件流有三个阶段: 事件捕获阶段,目标阶段,事件冒泡阶段。
根据W3C在2002年发布的标准,规定浏览器应该同时支持事件冒泡和事件捕获,首先事件以捕获(爷爷 => 父亲 => 儿子)的方式传递,然后以冒泡(儿子 => 父亲 => 爷爷)的方式传递。它们传递的时候会看是否有监听函数,有则调用并提供信息,没有就跳过。
在有父子关系的元素中,先监听捕获,再监听冒泡; 当只有一个div,不存在父子关系的时候,谁先监听就先执行谁。
三、冒泡和捕获
-
事件捕获(Capture Phase):浏览器从根节点开始由外到内到达目标节点顺序调用监听函数。
-
事件冒泡(Bubbling Phase):顺序同捕获相反,从目标节点到根节点顺序调用监听函数。
可以通过addEventListener的第三个参数决定函数处于捕获阶段还是冒泡阶段:
element.addEventLisenter('click',fn,true) // true按捕获方向执行函数
element.addEventLisenter('click',fn,false) // false按冒泡方向执行函数
四、取消冒泡
捕获是不可被取消的,但冒泡可以:
button.addEventListener('click',(e) => {
console.log('button click')
e.stopPropagation()
})
一般用于封装特定需求的独立组件。
五、Target 和 currentTarget
- e.target 是用户操作的元素
- e.currentTarget 是程序员监听的元素
六、自定义事件
element.addEventListener('click', () => {
const event = new CustomEvent('custom', {
"detail": {name: "custom", age: 18}
})
element.dispatchEvent(event)
})
element.addEventListener('custom', (e) => {
console.log('custom')
console.log(e)
})
七、事件委托
利用事件冒泡机制把本该在元素自身响应的事件交给任意一个上级元素去处理,这种行为称之为事件委托或者事件代理。
优点是:1、省内存;2、可以监听动态元素,不论内部元素是增加还是减少。
setTimeout(() => {
const button = document.createElement("button");
const span = document.createElement("span");
span.textContent = "click 1";
button.appendChild(span);
div1.appendChild(button);
}, 1000);
function on(eventType,element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
let el = e.target;
while (!el.matches(selector)) {
if (element === el) {
el = null;
break;
}
el = el.parentNode;
}
el && fn.call(el,e,el);
});
return element;
}
// 使用方式
on("click","#div1","button", () => {
console.log("button被点击了");
});