一路翻、DOM事件流
- DOM事件流(event flow)存在三個(gè)階段:事件捕獲階段狈癞、處于目標(biāo)階段、事件冒泡階段茂契,事件冒泡和事件捕獲都是描述事件觸發(fā)時(shí)序問題的術(shù)語蝶桶;
- 事件捕獲(event capturing):當(dāng)鼠標(biāo)點(diǎn)擊或觸發(fā)dom事件時(shí),瀏覽器會從根節(jié)點(diǎn)開始由外到內(nèi)進(jìn)行事件傳播掉冶,即點(diǎn)擊了子元素真竖,如果父元素通過事件捕獲方式注冊了對應(yīng)事件的話,會先觸發(fā)父元素綁定的事件厌小,是自上而下的恢共;
事件冒泡(dubbed bubbling):與事件捕獲恰恰相反,事件冒泡順序是由內(nèi)到外進(jìn)行傳播璧亚,直到根節(jié)點(diǎn)讨韭,是自下而上的; - 無論是事件捕獲還是事件冒泡涨岁,它們都有一個(gè)共同的行為拐袜,就是事件傳播,它就像一根引線梢薪,只有通過引線才能將綁在引線上的鞭炮(事件監(jiān)聽器)引爆蹬铺;
三個(gè)階段:1-5捕獲過程、5-6目標(biāo)過程秉撇、6-10冒泡過程
二甜攀、事件綁定方法:addEventListener、attachEvent - addEventListener
document.getElementById("child").addEventListener("click", function(e) {
console.log("child事件被觸發(fā)琐馆," + this.id)
},true)
- attachEvent
element.attachEvent('onclick', function(){
// ...
});
三规阀、事件冒泡
- 綁定事件方法的第三個(gè)參數(shù),就是控制事件觸發(fā)順序是否為事件捕獲瘦麸,true為事件捕獲谁撼,false為事件冒泡,默認(rèn)為false即事件冒泡滋饲,JQuery的e.stopPropagation會阻止冒泡厉碟,意思是到我為止喊巍,我的上一級的事件就不要觸發(fā)了;
// 事件冒泡
<body>
<div id="parent">
<div id="child" class="child">child</div>
</div>
<script type="text/javascript">
document.getElementById("parent").addEventListener("click", function(e) {
console.log("parent事件被觸發(fā)箍鼓," + this.id);
})
document.getElementById("child").addEventListener("click", function(e) {
console.log("child事件被觸發(fā)崭参," + this.id)
})
// child事件被觸發(fā),child
// parent事件被觸發(fā)款咖,parent
</script>
</body>
二何暮、事件捕獲
// 事件捕獲
<body>
<div id="parent">
<div id="child" class="child">child</div>
</div>
<script type="text/javascript">
document.getElementById("parent").addEventListener("click", function(e) {
console.log("parent事件被觸發(fā)," + this.id);
},true)
document.getElementById("child").addEventListener("click", function(e) {
console.log("child事件被觸發(fā)铐殃," + this.id)
},true)
// parent事件被觸發(fā)海洼,parent
// child事件被觸發(fā),child
</script>
</body>
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<style>
#parent {
width: 200px;
height: 200px;
text-align: center;
line-height: 3;
background: green;
}
#child {
width: 100px;
height: 100px;
margin: 0 auto;
background: orange;
}
</style>
</head>
<body>
<div id="parent">
父元素
<div id="child">
子元素
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.body.addEventListener("click", function(e) {
console.log("click-body");
}, false);
parent.addEventListener("click", function(e) {
console.log("click-parent");
}, false);
//新增事件捕獲事件代碼
parent.addEventListener("click", function(e) {
console.log("click-parent--事件捕獲");
}, true);
child.addEventListener("click", function(e) {
console.log("click-child");
// e.stopPropagation(); // 停止事件傳播
}, false);
</script>
</body>
</html>
<style type="text/css">
#wrapDiv, #innerP, #textSpan{
margin: 5px;padding: 5px;box-sizing: border-box;cursor: default;
}
#wrapDiv{
width: 300px;height: 300px;border: indianred 3px solid;
}
#innerP{
width: 200px;height: 200px;border: hotpink 3px solid;
}
#textSpan{
display: block;width: 100px;height: 100px;border: orange 3px solid;
}
</style>
</head>
<body>
<div id="wrapDiv">wrapDiv
<p id="innerP">innerP
<span id="textSpan">textSpan</span>
</p>
</div>
<script>
var wrapDiv = document.getElementById("wrapDiv");
var innerP = document.getElementById("innerP");
var textSpan = document.getElementById("textSpan");
// 捕獲階段綁定事件
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);
// 冒泡階段綁定的事件
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>
</body>
- target和currentTarget都是event上面的屬性背稼,target是真正發(fā)生事件的DOM元素贰军,而currentTarget是當(dāng)前事件發(fā)生在哪個(gè)DOM元素上;目標(biāo)階段也就是 target == currentTarget的時(shí)候蟹肘,由于window沒有nodeName這個(gè)屬性词疼,所以是undefined;
-
在目標(biāo)元素上不會遵守先發(fā)生捕獲后發(fā)生冒泡這一規(guī)則帘腹,而是先綁定的事件先發(fā)生贰盗;
五、事件委托
var parent = document.getElementById("parent");
var child = document.getElementById("child");
parent.onclick = function(e) {
if (e.target.id == "child") {
console.log("您點(diǎn)擊了child元素")
}
if (e.target.id == "parent") {
console.log("您點(diǎn)擊了parent元素")
}
}
六阳欲、事件綁定的方法
- 直接獲取元素綁定:
element.onclick = function(e){
// ...
};
優(yōu)點(diǎn):簡單和穩(wěn)定舵盈,可以確保它在你使用的不同瀏覽器中運(yùn)作一致;處理事件時(shí)球化,this關(guān)鍵字引用的是當(dāng)前元素秽晚,這很有幫組;
缺點(diǎn):只會在事件冒泡中運(yùn)行;一個(gè)元素一次只能綁定一個(gè)事件處理函數(shù)筒愚,新綁定的事件處理函數(shù)會覆蓋舊的事件處理函數(shù)赴蝇;事件對象參數(shù)(e)僅非IE瀏覽器可用;
- 直接在元素里面使用事件屬性:
- W3C方法:
element.addEventListener('click', function(e){
// ...
}, false);
優(yōu)點(diǎn):該方法同時(shí)支持事件處理的捕獲和冒泡階段;事件階段取決于addEventListener最后的參數(shù)設(shè)置:false (冒泡) 或 true (捕獲)巢掺;在事件處理函數(shù)內(nèi)部句伶,this關(guān)鍵字引用當(dāng)前元素;事件對象總是可以通過處理函數(shù)的第一個(gè)參數(shù)(e)捕獲陆淀;可以為同一個(gè)元素綁定你所希望的多個(gè)事件考余,同時(shí)并不會覆蓋先前綁定的事件;
缺點(diǎn):IE不支持,你必須使用IE的attachEvent函數(shù)替代;
- IE下的方法:
element.attachEvent('onclick', function(){
// ...
});
優(yōu)點(diǎn):可以為同一個(gè)元素綁定你所希望的多個(gè)事件轧苫,同時(shí)并不會覆蓋先前綁定的事件;
缺點(diǎn):IE僅支持事件捕獲的冒泡階段楚堤;事件監(jiān)聽函數(shù)內(nèi)的this關(guān)鍵字指向了window對象,而不是當(dāng)前元素(IE的一個(gè)巨大缺點(diǎn));事件對象僅存在與window.event參數(shù)中身冬;事件必須以ontype的形式命名鳄袍,比如,onclick而非click吏恭;僅IE可用,你必須在非IE瀏覽器中使用W3C的addEventListener重罪;
注意:不是意味這低版本的ie沒有事件捕獲樱哼,它也是先發(fā)生事件捕獲,再發(fā)生事件冒泡剿配,只不過這個(gè)過程無法通過程序控制搅幅;
七、解除事件
element.removeEventListener('click', function(e){
// ...
}, false);
IE:
element.detachEvent('onclick', function(){
// ...
});
八呼胚、阻止事件
在支持addEventListener()的瀏覽器中茄唐,可以調(diào)用事件對象的stopPropagation()方法以阻止事件的繼續(xù)傳播,如果在同一對象上定義了其他處理程序蝇更,剩下的處理程序?qū)⒁琅f被調(diào)用沪编,但調(diào)用stopPropagation()之后任何其他對象上的事件處理程序?qū)⒉粫徽{(diào)用,不僅可以阻止事件在冒泡階段的傳播年扩,還能阻止事件在捕獲階段的傳播蚁廓;
IE9之前的IE不支持stopPropagation()方法,而是設(shè)置事件對象cancelBubble屬性為true來實(shí)現(xiàn)阻止事件進(jìn)一步傳播厨幻;
九相嵌、阻止事件的默認(rèn)行為
e.preventDefault()可以阻止事件的默認(rèn)行為發(fā)生,默認(rèn)行為是指:點(diǎn)擊a標(biāo)簽就轉(zhuǎn)跳到其他頁面况脆、拖拽一個(gè)圖片到瀏覽器會自動(dòng)打開饭宾、點(diǎn)擊表單的提交按鈕會提交表單等等,因?yàn)橛械臅r(shí)候我們并不希望發(fā)生這些事情格了,所以需要阻止默認(rèn)行為看铆;
IE9之前的IE中,可以通過設(shè)置事件對象的returnValue屬性為false達(dá)到同樣的效果笆搓;
function cancelHandler(event){
var event=event||window.event;//兼容IE
//取消事件相關(guān)的默認(rèn)行為
if(event.preventDefault) //標(biāo)準(zhǔn)技術(shù)
event.preventDefault();
if(event.returnValue) //兼容IE9之前的IE
event.returnValue=false;
return false; //用于處理使用對象屬性注冊的處理程序
}