JS事件传播的两种机制包括冒泡和捕获,下面将具体剖析它们之间本质的区别。
事件冒泡: 先触发子元素的事件,再触发父元素的事件。
创建一个 ul label 和 li label, 分别绑定一个父id 和 子 id, 再通过创建 script,去绑定各自的点击事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="father">
我是一个无序列表
<li id="son">第1个列表项</li>
</ul>
<script>
document.getElementById('father').onclick = function(){
console.log('我点击了父元素');
}
document.getElementById('son').onclick = function(){
console.log('我点击了子元素');
}
</script>
</body>
</html>
当我点击 "第1个列表项"后,在Console先输出的是 “我点击了子元素”, 然后是 “我点击了父元素”, 可见冒泡的执行顺序是由里向外,也就是从 li - ul - body - document - window 这样的执行顺序,就好比人扔了一块石头去河里,先是冒一个小泡,再逐个现成大的水泡这种扩散现象。因此,JS默认的点击事件就是冒泡。
事件捕获: 先触发父元素的事件,再触发子元素的事件。如果要将冒泡改为捕获,需要添加监听事件。监听事件的第3个参数必须为 “true”, 默认为 false 。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="father">
我是一个无序列表
<li id="son">第1个列表项</li>
</ul>
<script>
// document.getElementById('father').onclick = function(){
// console.log('我点击了父元素');
// }
document.getElementById('father').addEventListener('click',function(){
console.log('我点击了父元素');
},true);
document.getElementById('son').onclick = function(){
console.log('我点击了子元素');
}
</script>
</body>
</html>
再点击 "第1个列表项"后,控制台先输出的是 “我点击了父元素”, 然后是 “我点击了子元素”, 可见捕获的执行顺序是由外向里,也就是从 window - documment - body - ul - li 这样的执行顺序。
事件代理是指利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
先复制多几个 li label
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="father">
我是一个无序列表
<li id="son">第1个列表项</li>
<li id="son">第2个列表项</li>
<li id="son">第3个列表项</li>
<li id="son">第4个列表项</li>
<li id="son">第5个列表项</li>
</ul>
<script>
// document.getElementById('father').onclick = function(){
// console.log('我点击了父元素');
// }
document.getElementById('father').addEventListener('click',function(){
console.log('tesing for 事件代理');
},true);
// document.getElementById('son').onclick = function(){
// console.log('我点击了子元素');
// }
</script>
</body>
</html>
例如然后点击 “第5个列表项”,由于冒泡作用,当点击到 li label,冒泡到 ul label 上,执行了 ul label上的点击事件,最后在 Console 输出了 “tesing for 事件代理”。
通常父级那么多子元素,怎样去区分事件本应该是哪个子元素呢?在 function() 函数里面添加一个 event param, 稍后在Console打印这个 event 的 object
例如当点击 “第4个列表项” 后,就会输出了 event 这个 object
点击 “target” 继续展开后续的内容
展开后,可见还有省略的内容,点击 “…” 这个省略的位置后,可以继续展示余下隐藏的内容。
展开后,可见 textContent : “第4个列表项”
添加判断条件为 点击 "第4个列表项"时,才会打印 “tesing for 事件代理”, 点击其它元素没有任何内容输出。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="father">
我是一个无序列表
<li id="son">第1个列表项</li>
<li id="son">第2个列表项</li>
<li id="son">第3个列表项</li>
<li id="son">第4个列表项</li>
<li id="son">第5个列表项</li>
</ul>
<script>
// document.getElementById('father').onclick = function(){
// console.log('我点击了父元素');
// }
document.getElementById('father').addEventListener('click',function(event){
if (event.target.textContent === "第4个列表项"){
console.log('tesing for 事件代理');
};
// console.log(event);
},true);
// document.getElementById('son').onclick = function(){
// console.log('我点击了子元素');
// }
</script>
</body>
</html>
怎么取消冒泡或者捕获 ? 通过 event.stopPropagation() 方法可以实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="father">
我是一个无序列表
<li id="son">第1个列表项</li>
<!-- <li id="son">第2个列表项</li>
<li id="son">第3个列表项</li>
<li id="son">第4个列表项</li>
<li id="son">第5个列表项</li> -->
</ul>
<script>
// document.getElementById('father').onclick = function(){
// console.log('我点击了父元素');
// }
// document.getElementById('father').addEventListener('click',function(event){
// // if (event.target.textContent === "第4个列表项"){
// // console.log('tesing for 事件代理');
// // };
// // // console.log(event);
// // },true);
// document.getElementById('son').onclick = function(){
// console.log('我点击了子元素');
// }
document.getElementById('father').addEventListener('click',function(event){
console.log('testing for click father element');
});
document.getElementById('son').addEventListener('click',function(event){
console.log('testing for click son element');
event.stopPropagation();
});
</script>
</body>
</html>
再次点击 “第1个列表项” 后,只执行子元素输出 “testing for click son element”,不再执行父元素, 也就是不再输出 “testing for click father element”, 取消了冒泡。