相信大家對事件流有一點的認識,在這裏我就通過一個例子來說明其中的細節。這是你知道的不知道的,不知道的知道的,笑死。
一、事件階段:
捕獲階段 目標階段 冒泡階段
二、案例分析
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);//無第三個參數
}