DOM 事件
冒泡:window 是终点,向上传播;
捕获:window 是起点,向下传播;
addEventListener 的第三个参数默认是 false 冒泡模式,true 则为捕获模式。不同模式,会影响到事件的传播路径,具体看后面的示例代码。
在元素上同时绑定捕获事件和冒泡事件,如果通过此元素的子级元素触发,则优先触发捕获事件,若不通过此元素的子级元素触发,则按照 Javascript 执行顺序触发。
事件干预
preventDefault()
阻止默认行为,比如 a 的跳转,上传 input 的弹框;
stopPropagation()
阻止冒泡,事件不再向上传播;
return false;
不能阻止默认行为,也不能阻止冒泡。
这个最容易出错,网上也很多错误的文章。
为什么出错?因为 jQuery 环境下,return false;
确实阻止了默认行为,还阻止了冒泡。
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DOM事件</title>
<style>
#grandfather {
width: 300px;
height: 300px;
background: #666;
}
#father {
width: 200px;
height: 200px;
background: #999;
}
#child {
width: 100px;
height: 100px;
background: #aaa;
}
</style>
</head>
<body>
<p>
addEventListener第三个参数默认为false冒泡模式,true则为捕获模式。
<br />
冒泡模式:window终点,向上传播;
<br />
捕获模式:window起点,向下传播
<br />
DOM事件的传播顺序是先捕获后冒泡;如果同时指定了捕获和冒泡事件,则取决于代码的先后顺序。
<br />
return false 并不能阻止冒泡,也不能阻止默认行为,只有开启干预模式才行。
</p>
<div id="grandfather">
grandfather
<div id="father">
father
<div id="child">
<a href="#child">child</a>
<input type="file" />
</div>
</div>
</div>
<div>
开启干预:<span id="state">false</span>
<button id="btn">切换</button>
</div>
<script>
let grandfather = document.getElementById('grandfather');
let father = document.getElementById('father');
let child = document.getElementById('child');
var btn = document.getElementById('btn');
var state = document.getElementById('state');
btn.addEventListener('click', function () {
state.innerText = getState() ? 'false' : 'true';
console.log('干预切换成功', state.innerText);
});
function getState() {
return state.innerText === 'true';
}
// 冒泡模式
child.addEventListener('click', function () {
if (getState()) {
event.preventDefault(); // 阻止了默认行为
console.log('child使用preventDefault阻止了默认行为,因此URL不会出现hash,文件弹框也不会弹出');
}
console.log('冒泡模式: child');
return false; // 并不能阻止冒泡,也不能阻止默认行为
});
father.addEventListener('click', function () {
if (getState()) {
event.stopPropagation(); // 阻止了冒泡
console.log('father使用stopPropagation阻止了冒泡,因此grandfather不会被触发');
}
console.log('冒泡模式: father');
return false; // 并不能阻止冒泡,也不能阻止默认行为
});
grandfather.addEventListener('click', function () {
console.log('冒泡模式: grandfather');
return false; // 并不能阻止冒泡,也不能阻止默认行为
});
// 捕获模式
grandfather.addEventListener(
'click',
function () {
console.log('捕获模式: grandfather');
},
true,
);
father.addEventListener(
'click',
function () {
console.log('捕获模式: father');
},
true,
);
child.addEventListener(
'click',
function () {
console.log('捕获模式: child');
},
true,
);
</script>
</body>
</html>
参考
https://stackoverflow.com/questions/30473581/when-to-use-preventdefault-vs-return-false