本篇文章参考以下博文
https://www.jianshu.com/p/d3e9b653fa95
事件冒泡
我们先象征性的举个例子,帮助大家有一个具像的理解,下面有三个盒子,他们嵌套在一起。
<div id="num1" onClick={console.log(1)}>
<div id="num2" onClick='console.log(2)'>
<div id="num3" onClick='console.log(3)'></div>
</div>
</div>
当点击黄色盒子的时候,控制台输出的结果为3,2,1。
虽然点击的是黄色盒子,但是浏览器不管你点的是谁,只关心你点的位置有几个元素,点击黄色区域,有三个元素,所以三个元素都被判定为点击,这个类似于你打了别人肚子一拳,虽然是打在他的衣服上面了,但是人家可不管那个,就判定你打他了。
事件冒泡的输出顺序比较好理解,点击的谁,就从谁开始,一层一层往外扩散,直到扩散到document
,还是上面的例子,当你打别人的时候,力量是先传递到衣服上,然后再传递到被打人的肉上,一层一层传递。
事件捕获
事件捕获正好和冒泡相反,修改一下代码
<div id="num1">
<div id="num2">
<div id="num3"></div>
</div>
</div>
<script>
let div1 = document.getElementById('num1');
div1.addEventListener('click', function () {
console.log(1)
}, true)
let div2 = document.getElementById('num2');
div2.addEventListener('click', function () {
console.log(2)
}, true)
let div3 = document.getElementById('num3');
div3.addEventListener('click', function () {
console.log(3)
}, true)
</script>
上面添加监听事件的方式是为了修改事件捕获,当addEventListener
的第三个参数为true
的时候,为事件捕获,默认为false
,这次我们还是点击黄色区域,输出结果如下:
这次顺序完全反了过来,可以理解为,程序在执行过程中,要从上往下执行代码,执行到最外层(粉色)div的时候,发现他有一个点击事件,鼠标点击的位置还正好在他的范围之内,那就先把他执行了,别的以后再说。
这样一层一层往里执行,找到谁就执行谁,所以执行顺序为1,2,3。
事件委托
事件委托的机制源于冒泡,现在有一个<ul>
,里面有1000个<li>
需要添加监听事件,这个时候就不能一个一个的给<li>
添加监听事件了,累死也加不完。
这时候就需要用到刚学习过的事件冒泡了,我们已经知道点击一个<li>
的时候,事件会冒泡到<ul>
上,那直接给<ul>
添加上点击事件就可以了,不管点到哪个<li>
上都能触发。
当然了,我们还是要区分到底是点击的哪个<li>
的,比如这个例子,<ul>
里面有1000个<li>
,当我点击其中一个<li>
的时候,希望它高亮显示。我们可以用以下方法实现
let ul = document.getElementById('ul');
ul.addEventListener('click', function (e) {
let id = e.target.getAttribute('id') //获取元素的id属性
let li = document.getElementById(id); //这里id没有加引号,是在使用上面刚刚获取到的id属性
li.classList.add("className1") //添加class属性,可以在className1中写上高亮样式
}, false)
阻止事件冒泡和捕获
有时候我们不想冒泡,点谁就谁执行,别人别捣乱
let ul = document.getElementById('ul');
ul.addEventListener('click', function (e) {
...
e.stopPropagation() //加上这个就可以阻止冒泡了,同样也适用于捕获
})
上面提到了e.target
,这个东西能帮我们获取到是谁触发的点击事件,但当使用一些第三方库的组件的时候,e.target
可能无法获取到我们想要的元素,这个时候需要使用e.currentTarget
,具体详情请查看以下博文