前言
本系列主要整理前端面试中需要掌握的知识点。本节介绍 JS中的事件模型和事件代理 。
文章目录
一、事件与事件流
事件:在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性,常见的有加载事件、鼠标事件、自定义事件。
事件流:由于DOM是个树结构,如果在父子节点绑定事件时候,当触发子节点的时候,就存在一个顺序的问题,这就是事件流。
事件流都会经历的三个阶段
- 事件捕获阶段;
- 处于目标阶段;
- 事件冒泡阶段。
事件冒泡:一种从下往上的传播方式,由最具体的元素然后逐渐向上传播到最不具体的那个节点,也就是DOM的高层的父节点。
<body>
<button id="btn">Click me!</button>
</body>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
console.log("1.Button");
}
document.body.onclick = function(){
console.log("2.Body");
}
document.onclick = function(){
console.log("3.document");
}
window.onclick = function(){
console.log("4.window");
}
</script>
点击后:
二、事件模型
1. 原始事件模型(DOM0级)
- HTML代码中直接绑定
<input type="button" onclick="fun()">
- 通过JS代码绑定
var btn = document.getElementById('.btn');
btn.onclick = fun;
特性
- 绑定速度快:但由于绑定速度太快,可能页面还未完全加载出来,以至于事件可能无法正常运行;
- 只支持冒泡,不支持捕获;
- 同一个类型的事件只能绑定一次
2. 标准事件模型(DOM2级)
- 事件捕获阶段:事件从document一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行;
- 事件处理阶段:事件到达目标元素,触发目标元素的监听函数;
- 事件冒泡阶段:事件从目标元素冒泡到document,依次检查经过的节点是否绑定了监听函数,如果有则执行。
- 绑定监听:
addEventListener(eventType, handler, useCapture)
- 移除监听:
removeEventListener(eventType, handler, useCapture)
var btn = document.getElementById('.btn');
btn.addEventListener(‘click’, showMessage, false);
btn.removeEventListener(‘click’, showMessage, false);
特性
- 可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突
btn.addEventListener(‘click’, showMessage1, false);
btn.addEventListener(‘click’, showMessage2, false);
btn.addEventListener(‘click’, showMessage3, false);
- 当第三个参数设置为true就在捕获过程中执行,反之在冒泡过程中执行
3. IE事件模型(基本不用)
- 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
- 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
- 绑定监听:
attachEvent(eventType, handler)
; - 移除监听:
detachEvent(eventType, handler)
; - 例如
var btn = document.getElementById('.btn');
btn.attachEvent(‘onclick’, showMessage);
btn.detachEvent(‘onclick’, showMessage);
三、事件委托
1. 事件委托是什么
事件委托:就是把一个元素响应事件的函数委托到另一个元素,具体来说,会把一个或者一组元素的事件委托到它的父层,或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素。
2. 事件委托的应用场景
如果有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件。如果给每个列表项都绑定事件的话,会有很多重复的操作,因此可以绑定他们的父元素。
document.getElementById('list').addEventListener('click',function(e){
var event = e || window.event;
var target = event.target || event.srcElement;
if(target.nodeName.toLocaleLowerCase() === 'li'){
console.log('the content is:',target.innerHTML);
}
})
动态添加元素:
<body>
<button id="btn">添加新元素</button>
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
<li>item 5</li>
</ul>
</body>
<script>
const btn = document.getElementById("btn");
const list = document.getElementById("list");
var num = 5;
list.onclick = function(e){
var event = e || window.event;
var target = event.target || event.srcElement;
if(target.nodeName.toLocaleLowerCase() === 'li'){
console.log('the content is:',target.innerHTML);
}
}
btn.onclick = function(){
num++;
const newlist = document.createElement('li');
newlist.innerHTML = `item ${num}`;
list.appendChild(newlist);
}
</script>
3. 事件委托的注意点
- 适合事件委托的事件有:click, mousedown, mouseup, keydown, keyup, keypress;
- focus, blur这些事件没有事件的冒泡机制,所以无法进行委托绑定事件;
- mousemove, mouseout这样的事件虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此不适合做事件委托;
- 如果把所有事件都用事件代理,可能会出现事件误判,即本不该被触发的事件被绑定上了事件。