JS事件冒泡机制和兼容性添加事件
本篇文章主要来讲讲 事件的冒泡机制 和 添加事件的几种方法。
一. JS的事件传递顺序: 捕获阶段 -> 目标阶段 -> 冒泡阶段
-
捕获阶段是指从父层往子层找。比如
<body><div></div></body>
,我们会先找到 body,然后下一个才是 div。就像中央领导发布政策一样。 从上往下通知。 -
目标阶段就是找到目标的那段时间,这个我们暂且不谈。
-
冒泡阶段就是从子层往外层传。比如
<body><div></div></body>
,这个时候是先写 div ,再找 body。往外依次冒泡。从内往外通知。先从外到内传一圈波纹,再从内到外传一圈波纹
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div > <div > 点我 </div> </div> </body> <script type="text/javascript"> const oBtn = document.getElementById('div1') const div1 = document.getElementById('div1') const div2 = document.getElementById('div2') // addEventListener 的第三个参数默认为 false —— 表示该事件在冒泡阶段触发; true 则表示在捕获阶段触发 div1.addEventListener('click', function (event) { console.log("A"); }, true); div2.addEventListener('click', function (event) { console.log("B"); }); div1.addEventListener('click', function (event) { console.log("C") }, false); div2.addEventListener('click', function (event) { console.log("D"); }, true); </script> </html>
答案是: DACB
根据 JS 的 dom 事件顺序 ———— “从外到内,从内到外” 和 addEventListener第三个参数所控制的触发阶段我们可以很容易地解答。
- 1.div2 在外面,div1 在里面。捕获阶段时优先;掠过 div2 后再掠过 div1,发现他们要找的人是 D 和 A (因为第三个参数为true)
- 2.然后到了 冒泡阶段;掠过 div1 后途经 div2;他们要找的分别是捆绑在 div1 上的 C 然后是 B。
二. 兼容性地添加 JS 事件
如果我们要给按钮添加事件,我们会怎么做呢?
也许我们会这么做<button onclick="console.log('1')">点我</button>
很明显地,它破坏了标签。我们应该让 html 和 事件分离,否则,我们下一次要修改这个按钮就不好操作了。
他有个致命的缺点:那就是只能用此方法添加一个事件,后面的会覆盖前面(原理是指向了不同的对象)。
- 错误示范:
const oBtn = document.getElementsByTagName('button')[0]; oBtn.onclick = function() { console.log('1'); } // 只会显示下面这次 oBtn.onclick = function() { console.log('我是第二次'); }
- 正确示范:
const oBtn = document.getElementsByTagName('button')[0]; // addEventListener oBtn.addEventListener("click", () => {console.log('1')}); oBtn.addEventListener("click", () => {console.log('我是第二次')} ); // 上面的两次都会被认可
我们已经决定放弃(不推荐)使用 onclick;但接下来还有一件事情要做,那就是兼容性问题。
事件并不总是如我们所想的运转,感觉失望和沮丧是徒劳无益的,我们要想办法改变我们自己适应这个世界。
IE 6/7/8 上并没有 addEventListener.它们使用了特殊的 attachEvent;对于这样的少数人,我们也不能完全放弃。
代码的健壮性是锦上添花还是...至关重要的?也许这是裁定程序员优秀的标准吧。
遂改成这样。(可以在老IE和Chrome上感受一下差距)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div >
<div >
点我
</div>
</div>
</body>
<script type="text/javascript">
var oBtn = document.getElementById('div1')
var div1 = document.getElementById('div1')
var div2 = document.getElementById('div2')
// 注意:attachEvent 只有冒泡没有捕获!!!
var addEvent = function (dom, eventType, method, isBubble) {
eventType = eventType || 'click'
isBubble = isBubble || false
if (dom.addEventListener) {
dom.addEventListener(eventType, method, isBubble)
} else if (dom.attachEvent) {
dom.attachEvent('on' + eventType, method)
}
}
var methodX = function () {
console.log('methodX')
}
var method2 = function () {
console.log('method2')
}
addEvent(div1, 'click', methodX)
addEvent(div2, 'click', method2)
</script>
</html>
除此之外我们还想要知道的 addEventListener 和 attachEvent 都是讲究规则的。谁先来,谁在上面。
// 多个 addEventListener 的执行顺序:谁先被添加,谁就在前面
div1.addEventListener('click', event => {
console.log('first')
})
div1.addEventListener('click', event => {
console.log('second')
})
div1.addEventListener('click', event => {
console.log('third')
})
complete