简介
事件冒泡和事件捕获是由微软和网景公司提出,这两个概念是为了解决页面中事件流(事件发生顺序)的问题。
假设三个div标签呈嵌套关系且这三个div上分别注册了相同的click事件。那么他们的触发顺序是怎样的呢?为了解决这个问题,微软和网景提出了分别提出了事件冒泡和事件捕获,两种几乎相反的概念。
<div id="test1">
test1
<div id="test2">
test2
<div id="test3">test3</div>
</div>
</div>
const divs = document.getElementsByTagName('div');
console.log('divs', divs);
//divs是伪数组,需要转换成真正的数组 console.log([...divs] instanceof Array); false
var flag = false; //事件冒泡
// var flag = true; //事件捕获
[...divs].forEach(item => {
item.addEventListener('click', f1, flag);
})
function f1(e) {
// e.stopPropagation(); //阻止事件冒泡和事件捕获
// e.cancelBubble = true; //不推荐使用 已经被移除web标准
console.log(this.id);
}
事件冒泡(event bubbling)
事件会从最内层的元素开始发生,一直向上传播,直到document对象。
点击最里层的黄色test3,控制台打印出:
事件捕获(event capturing)
与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
点击最里层的黄色test3,控制台打印出:
事件委托
事件委托是利用事件冒泡的原理。
一个简单的小例子,可以清楚的说明事件委托:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const LiList = document.getElementsByTagName('li');
[...LiList].forEach(li=>{
li.onclick = function(){
console.log(li.innerHTML);
}
})
</script>
代码中有3个li,每一个li上都注册了一个函数,那么这3个函数会被创建在内存中占用空间。这是对内存的浪费!
解决办法: 可以利用事件冒泡的原理,把事件注册在父元素ul上。 点击某个具体元素,会依次一级一级往上调用父级元素的同名事件,直到window.
代码:
const ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e){
e = e || window.event;
console.log(e.target); //li 实际点击的具体元素li
console.log(e.currentTarget); //ul 绑定事件的元素ul
if(e.target.nodeName.toLowerCase()==='li'){
console.log(e.target.innerHTML);
}
}
利用事件冒泡的原理,把事件注册在父元素上,这种处理方法叫做事件委托。 事件委托还有一个好处就是:当新增li时,不必在新增li上注册事件。