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”, 取消了冒泡。