JavaScript 事件冒泡、事件捕获和事件委托
以下在chrome、firefox执行通过。
事件流、事件冒泡、事件捕获
window 捕获 - document 捕获 - html 捕获 - body 捕获 - xxx - body 冒泡 - html 冒泡 - document 冒泡 - window 冒泡
对于最内层 xxx 元素事件,以click为例
onclick(直接在标签上写或者document.getElementById('xxx').onclick)事件在冒泡阶段执行,并且优先于 addEventListener('click',false)/addEventListener('click',false)。
addEventListener('click',false)与addEventListener('click',false),先添加的事件先执行。
【栗子(以click为例)】
onclick
addEventListener('click',true) // 捕获
addEventListener('click',false) // 冒泡
[第一组]
添加顺序:window 捕获 - document 捕获 - html 捕获 - body 捕获 - xxx onclick - xxx 捕获 - xxx 冒泡 - body 冒泡 - html 冒泡 - document 冒泡 - window 冒泡
执行顺序:window 捕获 - document 捕获 - html 捕获 - body 捕获 - xxx onclick - xxx 捕获 - xxx 冒泡 - body 冒泡 - html 冒泡 - document 冒泡 - window 冒泡
[第二组]
添加顺序:window 冒泡 - document 冒泡 - html 冒泡 - body 冒泡 - xxx onclick - xxx 冒泡 - xxx 捕获 - body 捕获 - html 捕获 - document 捕获 - window 捕获
执行顺序:html 捕获 - body 捕获 - xxx onclick - xxx 冒泡 - xxx 捕获 - body 冒泡 - html 冒泡 - document 冒泡 - window 冒泡
【只针对最内层xxx元素】
onclick最先,然后其他的谁先添加谁执行
[第一组]
添加顺序:xxx onclick - xxx 冒泡 - xxx 捕获
执行顺序:xxx onclick - xxx 冒泡 - xxx 捕获
[第二组]
添加顺序:xxx onclick - xxx 捕获 - xxx 冒泡
执行顺序:xxx onclick - xxx 捕获 - xxx 冒泡
[第三组]
添加顺序:xxx 捕获 - xxx onclick - xxx 冒泡
执行顺序:xxx onclick - xxx 捕获 - xxx 冒泡
[第四组]
添加顺序:xxx 冒泡 - xxx onclick - xxx 捕获
执行顺序:xxx onclick - xxx 冒泡 - xxx 捕获
【完整实例】
<!DOCTYPE html>
<html lang="en" id="html" onclick="log(this)">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
html {
color: #000;
text-align: center;
font-size: 20px;
}
body {
background: #eee;
padding: 20px;
margin: 0;
}
div {
background-color: #ddd;
height: 500px;
padding: 20px;
}
ul {
margin-top:500px;
background-color: #ccc;
padding: 20px;
list-style: none;
}
li {
height: 100px;
margin-top: 20px;
background-color: #bbb;
}
</style>
</head>
<body id="body" onclick="log(this)">body
<div id="div" onclick="log(this)">div
<ul id="ul" onclick="log(this)">ul
<li id="li1" onclick="log(this)">li1</li>
<li id="li2" onclick="log(this)">li2</li>
<li id="li3" onclick="log(this)">li3</li>
</ul>
</div>
<script>
let flag = true;
let flag2 = false;
window.addEventListener('click', function(event) {
console.log(flag + ' ' + 'window')
}, flag);
document.addEventListener('click', function(event) {
console.log(flag + ' ' + 'document')
}, flag);
document.documentElement.addEventListener('click', function(event) {
console.log(flag + ' ' + this.id)
}, flag);
document.body.addEventListener('click', function(event) {
console.log(flag + ' ' + this.id)
}, flag);
document.getElementById('div').addEventListener('click', function(event) {
console.log(flag + ' ' + this.id)
}, flag);
document.getElementById('ul').addEventListener('click', function(event) {
console.log(flag + ' ' + this.id)
}, flag);
document.getElementById('li1').addEventListener('click', function(event) {
console.log(flag + ' ' + this.id)
}, flag);
window.addEventListener('click', function(event) {
console.log(flag2 + ' ' + 'window')
}, flag2);
document.addEventListener('click', function(event) {
console.log(flag2 + ' ' + 'document')
}, flag2);
document.documentElement.addEventListener('click', function(event) {
console.log(flag2 + ' ' + this.id)
}, flag2);
document.body.addEventListener('click', function(event) {
console.log(flag2 + ' ' + this.id)
}, flag2);
document.getElementById('div').addEventListener('click', function(event) {
console.log(flag2 + ' ' + this.id)
}, flag2);
document.getElementById('ul').addEventListener('click', function(event) {
console.log(flag2 + ' ' + this.id)
}, flag2);
document.getElementById('li1').addEventListener('click', function(event) {
console.log(flag2 + ' ' + this.id)
}, flag2);
function log(element) {
console.log('click ' + element.id)
}
</script>
</body>
</html>
点击li1,执行结果
true window
true document
true html
true body
true div
click ul
true ul
false ul
click div
false div
click body
false body
click html
false html
false document
false window
事件委托
点击绑定事件本身元素,按照 onclick 优先,冒泡捕获绑定事件按照先后顺序执行
点击子元素时,按照冒泡-onclick-捕获顺序执行
【完整实例】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
ul {
background-color: #ccc;
padding: 40px;
list-style: none;
}
li {
height: 100px;
margin-top: 20px;
background-color: #bbb;
}
button {
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<ul id="ul" onclick="operate(this,event)">
<li id="li1">
<button id="btn1">btn1</button>
</li>
<li id="li2">
<button id="btn2">btn2</button>
</li>
<li id="li3">
<button id="btn3">btn3</button>
</li>
</ul>
<script>
function operate(element, event) {
let log = function(c) {
console.log('click ' + c)
}
// log(element.id)
// log(event)
// log(event.type)
// log(event.path)
log(event.target.id)
// log(event.srcElement.id)
// log(event.toElement.id)
}
document.getElementById('ul').addEventListener('click', function(event) {
let log = function(c) {
console.log('捕获 ' + c)
}
// log(this.id)
// log(event)
// log(event.type)
// log(event.path)
log(event.target.id)
// log(event.srcElement.id)
// log(event.toElement.id)
}, false);
document.getElementById('ul').addEventListener('click', function(event) {
let log = function(c) {
console.log('冒泡 ' + c)
}
// log(this.id)
// log(event)
// log(event.type)
// log(event.path)
log(event.target.id)
// log(event.srcElement.id)
// log(event.toElement.id)
}, true);
</script>
</body>
</html>
[点击 ul]
click ul
捕获 ul
冒泡 ul
[点击 li1]
冒泡 li1
click li1
捕获 li1
[点击 li2]
冒泡 li2
click li2
捕获
[点击 btn1]
冒泡 btn1
click btn1
捕获 btn1
[点击 btn2]
冒泡 btn2
click btn2
捕获 btn2