什么是事件委托
事件委托就是将子级事件委托给父级,父级监听这个事件,如果事件触发,父级会通过 event.target
来获取产生事件的 DOM
为什么要用事件委托
举个例子,假设我们有一个列表,要求点击列表项弹出对应的字段
<ul id="myLink">
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
</ul>
如果不使用事件委托,我们需要这样写
let myLink = document.getElementById("myLink");
let li = document.getElementsByTagName("li");
for (let i = 0; i < li.length; i++) {
li[i].onclick = function(event) {
let e = event || window.event;
let target = e.target || e.srcElement;
alert(e.target.id + ":" + e.target.innerText);
};
}
这样做有两个缺点
- 会影响性能:当我们给 li 添加事件时,需要不断和 DOM 节点进行交互,访问 DOM 节点的次数多,引起浏览器 重排 和 重绘 的次数也就多。如果用到事件委托,与 DOM 的操作就减少到一次
- 占用的内存更多:当我们需要对 n 个 li 加事件时,需要的内存假设是 n,当我们只对父级这一个对象进行操作时,需要的内存只需要 1
所以,当数据量很大时,事件委托会大大的提高浏览器的性能
如何进行事件委托
let myLink = document.getElementById("myLink");
myLink.onclick = function(event) {
let e = event || window.event;
let target = e.target || e.srcElement;
if(e.target.nodeName.toLowerCase() == 'li') {
alert(e.target.id + ':' + e.target.innerText);
};
}
事件委托的原理是什么
事件委托的原理是 事件冒泡
那么什么是事件冒泡?
事件会从内层元素开始,一直传播到最外层元素
p -> div -> body -> html -> document
事件冒泡有一个相对概念叫做 事件捕获
事件会从最外层开始发生,直到内层具体的元素。
document -> html -> body -> div -> p
addEventListener
在 js 中 addEventListener
可以给一个 dom 对象添加事件监听
domElement.addEventListener("click", function(){}, true);
它有三个参数
- 第一个是事件类型,比如单击(click),双击(dbclick)
- 第二个是事件触发后需要执行的函数
- 第三个参数就是指定事件处理函数处理的时期, true 为捕获,false 为冒泡,默认 false
举个例子,我们有三个重叠的 div,并且每个 div 都绑定了事件
<div id="div1" style="width: 300px; height: 300px; background-color:aquamarine;">
div1
<div id="div2" style="width: 200px; height: 200px; background-color:burlywood;">
div2
<div id="div3" style="width: 100px; height: 100px; background-color:cornflowerblue">
div3
</div>
</div>
</div>
事件冒泡:
// 冒泡
document.getElementById('div1').addEventListener('click', div1SayHello)
document.getElementById('div2').addEventListener('click', div2SayHello)
document.getElementById('div3').addEventListener('click', div3SayHello)
点击 div3
点击 div2
点击 div1
事件捕获:
// 捕获
document.getElementById('div1').addEventListener('click', div1SayHello, true)
document.getElementById('div2').addEventListener('click', div2SayHello, true)
document.getElementById('div3').addEventListener('click', div3SayHello, true)
点击 div3
点击 div2
点击 div1
阻止冒泡
event.stopPropagation
function preventBubbles(event) {
var e = event || window.event;
if(e && e.stopPropagation) {
//非IE浏览器
e.stopPropagation();
} else {
//IE浏览器(IE11以下)
e.cancelBubble = true;
};
console.log("div3~~~~~")
}
document.getElementById('div1').addEventListener('click', div1SayHello)
document.getElementById('div2').addEventListener('click', div2SayHello)
document.getElementById('div3').addEventListener('click', preventBubbles);
现在点击 div3
阻止默认行为
event.preventDefault
这个方法可以阻止默认行为,但是并不会阻止冒泡
<div id="info">
<a href="www.baidu.com" id="test">阻止默认事件</a>
</div>
document.getElementById('info').addEventListener('click', function(){ console.log("~~~~"); })
document.getElementById('test').addEventListener('click', preventEvent);
function preventEvent(event) {
if(event && event.preventDefault) {
//非IE浏览器
event.preventDefault();
} else {
//IE浏览器(IE11以下)
window.event.returnValue = false;
};
}
此时页面
点击连接,页面并不会跳转
此时控制台
参考文章
https://segmentfault.com/a/1190000012313105
https://segmentfault.com/a/1190000022084049
https://juejin.im/post/5cc941436fb9a03236394027
https://juejin.im/post/5acb1bcf6fb9a028dc414fc6#heading-1