Hello,最近又准备面试了,通过前几次面试经历,有很深刻的感受,一定要注重基础,注重基础,注重基础!
不要觉得自己用了一些轮子写了一些看起来很不错的项目和功能,就一定能找到好工作。那些轮子不是你写的,你只是一个搬运工…
我们要养成一个好的习惯,不要觉得我用这个轮子写了这个功能,这个功能就是我实现的,我就很厉害,厉害的是轮子,和你没关系,你做的事情,几乎所有人都能做。
最近复习到了原生JS的部分,这期给大家带来事件冒泡和事件代理。这部分的内容面试也经常遇到,在大厂面试中出现的频率也很频繁。
事件冒泡的来龙去脉
啥是事件冒泡呢,我们以一个简单的例子了解一下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
width: 200px;
height: 200px;
border: 1px solid #000;
}
.child {
width: 100px;
height: 100px;
}
.child.child-1 {
background-color: red;
}
.child.child-2 {
background-color: orange ;
}
</style>
</head>
<body>
<div class="father">
<div class="child child-1"></div>
<div class="child child-2"></div>
</div>
<script>
var oFather = document.getElementsByClassName('father')[0],
oChild1 = document.getElementsByClassName('child-1')[0],
oChild2 = document.getElementsByClassName('child-2')[0];
oFather.onclick = function() {
console.log('father');
}
oChild1.onclick = function() {
console.log('child-1');
}
oChild2.onclick = function() {
console.log('child-2');
}
</script>
</body>
</html>
上面的代码,我写了一个
div
为
father
,内容有两个同级
div
,分别为
child-1
和
child-2
。
并且我分别给它们添加了样式。
在浏览器中,它的渲染结果如下:
然后我写了一些JS代码,通过
DOM
操作,分别拿到三个
div
的节点,然后分别绑定各自点击事件的处理函数。
那么我现在点击一下网页中的红色区域,应该输出什么呢?
正常的思路应该觉得肯定输入
child-1
呀,因为红色的区域代表
div
的
class
属性为
child child-1
的部分。
那么我们看一下实际的效果。
控制台先输出了
child-1
,然后输出了
father
这是什么原因呢?
肯定有同学会说,因为
child-1
在
father
的内部,你点击了
child-1
也相当于点击了
father
ok,思路很清晰,那么我稍微改一下CSS代码。
现在
child-1
child-2
在视图上脱离了
father
容器,我继续点击红色区域会输出什么?
还是相同的结果。
又有同学要说了,虽然你在视图上让
child-1
child-2
脱离了
father
,但是在实际的HTML结构中,
child-1
child-2
仍然属于
father
的子标签。
ok,没有问题,这种思路完全没有问题,但是这样的思路不利于我们学习事件冒泡的原理。
我们换一个思路思考一下。
我在
body
上添加一个点击事件的处理函数,并且进行输出呢。我们再次点击
child-1
的时候,会不会触发
body
标签的输出?
没错,
body
的点击事件也会被触发。
我们按照这个思路,继续向上寻找,给
html
标签也绑定,给
document
也绑定,最后给
window
也绑定上点击事件的处理函数。
果然,这个点击事件的处理函数,被逐级触发~~~~
是不是很奇妙,虽然按照之前的思路,也可以解释的过去,但是还是希望大家以事件冒泡的思路去理解这个问题。
其实在很多的业务开发中,都会碰到类似的问题,我只是想让它自己的点击事件触发,但是它父类的点击事件也会跟着不小心触发,这就是烦恼。
那么我们介绍完事件冒泡的来龙去脉之后,我们要如何避免它呢?
避免事件冒泡的产生
我们在
oChild1
的点击事件处理函数中,添加一个参数
e
,并且在函数内部,输出这个
e
。然后看下控制台。
有一个
MouseEvent
的对象被打印出来。
我们试着跟踪一下它的原型链。
我们可以看到,跟着它的原型链走,分别经历了
MouseEvent -> mouseEvent -> UIEvent -> Event -> Object
我们着重看一下,在
Event
对象中,有一个
stopPropagation
方法,这个就是停止事件冒泡的方法,然后
cancelBubble
属性,默认是
false
这两个都可以停止事件冒泡。
它们的区别主要是
stopPropagation()
是符合
W3C
标准的,也就是说,大部分的浏览器都可以使用,
cancelBubble
是
IE
浏览器中处理事件冒泡的属性。
所以,在处理浏览器兼容的时候,它们才会有区别,功能是一摸一样的。
oChild1.onclick = function(e) {
console.log(e);
var e = e || window.event;
if (e.stopPropagation) {
e.stopPropagation();
}else {
e.cancelBubble = true;
}
console.log('child-1');
}
这样写,就是最标准的,考虑兼容的,处理事件冒泡的方法。
经过我们的处理,现在再次点击红色区域的时候,它只会输出点击事件处理函数中的输出语句,不会向上冒泡。
ok,以上就是关于事件冒泡的来龙去脉以及处理方法。