相信大家对事件流有一点的认识,在这里我就通过一个例子来说明其中的细节。这是你知道的不知道的,不知道的知道的,笑死。
一、事件阶段:
捕获阶段 目标阶段 冒泡阶段
二、案例分析
1.html部分
<div id='wrapDiv'>wrapDiv
<p id="innerP">innerP
<span id="textSpan">textSpan</span>
</p>
</div>
2.css部分
<style>
#wrapDiv,
#innerP,
#textSpan {
box-sizing: border-box;
/**width+padding+border**/
margin: 40px;
cursor: pointer;
}
#wrapDiv {
width: 300px;
height: 300px;
border: crimson solid 3px;
}
#innerP {
width: 200px;
height: 200px;
border: green solid 3px;
}
#textSpan {
width: 100px;
height: 100px;
border: blue solid 3px;
display: block;
}
</style>
3.js部分
var wrapDiv=document.getElementById('wrapDiv');
var innerP=document.getElementById('innerP');
var textSpan=document.getElementById('textSpan');
// Event.currentTarget指向事件绑定的元素,而 Event.target 则是事件触发的元素。
// true 表示捕获阶段
window.addEventListener('click',function(e){
console.log('window捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
document.addEventListener('click',function(e){
console.log('document捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
document.documentElement.addEventListener('click',function(e){
console.log('documentElement捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
document.body.addEventListener('click',function(e){
console.log('body捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
wrapDiv.addEventListener('click',function(e){
console.log('wrapDiv捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
innerP.addEventListener('click',function(e){
console.log('innerP捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
textSpan.addEventListener('click',function(e){
console.log('textSpan捕获',e.target.nodeName,e.currentTarget.nodeName);
},true);
// false 表示冒泡阶段
window.addEventListener('click',function(e){
console.log('window冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
document.addEventListener('click',function(e){
console.log('document冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
document.documentElement.addEventListener('click',function(e){
console.log('documentElement冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
document.body.addEventListener('click',function(e){
console.log('body冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
wrapDiv.addEventListener('click',function(e){
console.log('wrapDiv冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
innerP.addEventListener('click',function(e){
console.log('innerP冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
textSpan.addEventListener('click',function(e){
console.log('textSpan冒泡',e.target.nodeName,e.currentTarget.nodeName);
},false);
4.页面效果
问题来了来了!!!
(1).当点击span结果会是什么?(先捕获后冒泡 顺序看打印结果)
(2).如果将目标元素的冒泡和捕获绑定顺序互换 (打印结果出了目标元素的冒泡和捕获是根据绑定的顺序有关(谁先绑定谁先触发),所以在目标阶段 不一定是先捕获后冒泡)
// true 表示捕获阶段
window.addEventListener('click', function (e) {
//window无nodeName所以打印出来是undefined,不要见怪哦。
console.log('window捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.addEventListener('click', function (e) {
console.log('document捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.body.addEventListener('click', function (e) {
console.log('body捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
innerP.addEventListener('click', function (e) {
console.log('innerP捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
textSpan.addEventListener('click', function (e) {
console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
// 将被点击元素提前
textSpan.addEventListener('click', function (e) {
console.log('textSpan捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
// false 表示冒泡阶段
window.addEventListener('click', function (e) {
console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.addEventListener('click', function (e) {
console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.body.addEventListener('click', function (e) {
console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
innerP.addEventListener('click', function (e) {
console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
(3).如果给元素绑定onclick呢,和addEventLister又有什么区别呢?
A:onclick这样的事件,给同一个元素,同一个事件绑定多个处理函数后面会覆盖前面的
B:addEventLister 就可以实现同一个元素,同一个事件对应多个处理函数,不会覆盖
C:onclick和addEventLister混合使用,不会影响
D.onclick可以统一看做是在冒泡阶段,为什么这么说看下面例子
<script>
var wrapDiv = document.getElementById('wrapDiv');
var innerP = document.getElementById('innerP');
var textSpan = document.getElementById('textSpan');
// Event.currentTarget指向事件绑定的元素,而 Event.target 则是事件触发的元素。
// 添加onclick事件
wrapDiv.onclick = function (e) {
console.log('使用wrapDiv.onclick,直接绑定的元素,但不是直接触发的元素:在冒泡阶段触发', e.target.nodeName, e.currentTarget.nodeName);
}
textSpan.onclick = function (e) {
console.log('使用textSpan.onclick,既是直接绑定的元素,又是直接触发的元素:发生在哪个阶段是根据它和绑定捕获的位置有关,谁在前谁先触发', e.target.nodeName, e.currentTarget.nodeName);
}
// true 表示捕获阶段
window.addEventListener('click', function (e) {
console.log('window捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.addEventListener('click', function (e) {
console.log('document捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.body.addEventListener('click', function (e) {
console.log('body捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
innerP.addEventListener('click', function (e) {
console.log('innerP捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
textSpan.addEventListener('click', function (e) {
console.log('textSpan捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
// false 表示冒泡阶段
window.addEventListener('click', function (e) {
console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.addEventListener('click', function (e) {
console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.body.addEventListener('click', function (e) {
console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
innerP.addEventListener('click', function (e) {
console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
textSpan.addEventListener('click', function (e) {
console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
</script>
(4).阻止事件传播(e.stopPropagation();阻止冒泡,其实也可以阻止捕获)
// true 表示捕获阶段
window.addEventListener('click', function (e) {
console.log('window捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.addEventListener('click', function (e) {
console.log('document捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
document.body.addEventListener('click', function (e) {
console.log('body捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv捕获', e.target.nodeName, e.currentTarget.nodeName);
//在捕获阶段阻止事件的传播
e.stopPropagation();
// 或使用
// e.cancelBubble = true; //这个属性的规范并未统一 少用,知道就行
}, true);
innerP.addEventListener('click', function (e) {
console.log('innerP捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
textSpan.addEventListener('click', function (e) {
console.log('textSpan捕获', e.target.nodeName, e.currentTarget.nodeName);
}, true);
// false 表示冒泡阶段
window.addEventListener('click', function (e) {
console.log('window冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.addEventListener('click', function (e) {
console.log('document冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.documentElement.addEventListener('click', function (e) {
console.log('documentElement冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
document.body.addEventListener('click', function (e) {
console.log('body冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
wrapDiv.addEventListener('click', function (e) {
console.log('wrapDiv冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
innerP.addEventListener('click', function (e) {
console.log('innerP冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
textSpan.addEventListener('click', function (e) {
console.log('textSpan冒泡', e.target.nodeName, e.currentTarget.nodeName);
}, false);
(5).拓展:js中preventDefault()阻止事件的默认行为,而不是在阻止冒泡
// 默认行为是指:点击a标签就跳转,拖拽一个图片到浏览器会自动打开,点击表单的提交按钮会提交表单
e.preventDefault();
//或
//return false;
(6).addEventListener兼容问题(使用attachEvent)
if (el.addEventListener) {
el.addEventListener('click', modifyText, false);
} else if (el.attachEvent) { //兼容IE9之前
el.attachEvent('onclick', modifyText);//无第三个参数
}