javascript
html点击冒泡事件,JavaScript 浏览器事件机制(捕获、冒泡、委托)
DOM 樹(shù)
文檔對(duì)象模型 (DOM),我們把它簡(jiǎn)單的理解成一個(gè)對(duì)象const DOMTree = {}。它是瀏覽器的解析引擎把HTML文檔解析成相應(yīng)的JavaScript對(duì)象。
About elkThe truth about elk.
復(fù)制代碼
最終會(huì)解析成的樹(shù)狀結(jié)構(gòu)對(duì)象:
DOM 對(duì)象提供了一些接口讓我們可以對(duì)節(jié)點(diǎn)進(jìn)行增刪改查。并且還可以讓我們對(duì)節(jié)點(diǎn)進(jìn)行綁定事件。
例如我們可以對(duì)
節(jié)點(diǎn)綁定一個(gè)點(diǎn)擊事件。當(dāng)用戶觸發(fā)了點(diǎn)擊事件后,可以讓節(jié)點(diǎn)響應(yīng)一些事情。DOM 事件
DOM 事件提供了人與web進(jìn)行交互的一種方式。
事件流
如果你單擊了某個(gè)按鈕,他們都認(rèn)為單擊事件不僅僅發(fā)生在按鈕上。換句話說(shuō),在單擊按鈕的同時(shí),你也單擊了按鈕的容器元素,甚至也單擊了整個(gè)頁(yè)面。
事件流描述的是從頁(yè)面中接收事件的順序。但有意思的是,IE和Netscape開(kāi)發(fā)團(tuán)隊(duì)居然提出了差不多是完全相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕獲流。
事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件開(kāi)始時(shí)由最具體的元素(文檔中嵌套層次最深的那個(gè)節(jié)點(diǎn))接收,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)(文檔)。
點(diǎn)擊我復(fù)制代碼
click 事件首先在
元素上發(fā)生,而這個(gè)元素就是我們單擊的元素。然后,click事件沿DOM樹(shù)向上傳播,在每一級(jí)節(jié)點(diǎn)上都會(huì)發(fā)生,直至傳播到document對(duì)象。圖片展示了事件冒泡的過(guò)程[注意]所有現(xiàn)代瀏覽器都支持事件冒泡。
事件捕獲
事件捕獲的思想是不太具體的節(jié)點(diǎn)應(yīng)該更早接收到事件,而最具體的節(jié)點(diǎn)應(yīng)該最后接收到事件。事件捕獲的用意在于在事件到達(dá)預(yù)定目標(biāo)之前捕獲它。
在事件捕獲過(guò)程中,document對(duì)象首先接收到click事件,然后事件沿DOM樹(shù)依次向下,一直傳播到事件的實(shí)際目標(biāo),即
元素。圖片展示了事件捕獲的過(guò)程。DOM 事件流
“DOM2級(jí)事件”規(guī)定的事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段和事件冒泡階段。首先發(fā)生的是事件捕獲,為截獲事件提供了機(jī)會(huì)。然后是實(shí)際的目標(biāo)接收到事件。最后一個(gè)階段是冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。
在DOM事件流中,實(shí)際的目標(biāo)(
元素)在捕獲階段不會(huì)接收到事件。這意味著在捕獲階段,事件從document到再到后就停止了。下一個(gè)階段是“處于目標(biāo)”階段,于是事件在
上發(fā)生,并在事件處理中被看成冒泡階段的一部分。然后,冒泡階段發(fā)生,事件又傳播回文檔。
多數(shù)支持DOM事件流的瀏覽器都實(shí)現(xiàn)了一種特定的行為;即使“DOM2級(jí)事件”規(guī)范明確要求捕獲階段不會(huì)涉及事件目標(biāo),但I(xiàn)E9、Safari、Chrome、Firefox和Opera 9.5及更高版本都會(huì)在捕獲階段觸發(fā)事件對(duì)象上的事件。結(jié)果,就是有兩個(gè)機(jī)會(huì)在目標(biāo)對(duì)象上面操作事件。
[兼容]IE9、Opera、Firefox、Chrome和Safari都支持DOM事件流;IE8及更早版本不支持DOM事件流。
事件處理程序
事件就是用戶或?yàn)g覽器自身執(zhí)行的某種動(dòng)作。諸如 click、load 和 mouseover,都是事件的名字。 而響應(yīng)某個(gè)事件的函數(shù)就叫做事件處理程序(或事件偵聽(tīng)器)。
HTML 事件處理程序
復(fù)制代碼
html中定義的事件有權(quán)訪問(wèn)全局作用域中的任何代碼,例如外部引入的JS文件
DOM0級(jí)事件處理程序
以DOM0級(jí)方式添加的事件處理程序會(huì)在事件流的冒泡階段被處理
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};
復(fù)制代碼
使用DOM0級(jí)方法指定的事件處理程序被認(rèn)為是元素的方法。因此,這時(shí)候的事件處理程序是在元素的作用域中運(yùn)行;
DOM0級(jí)事件處理程序的缺點(diǎn)是圍繞著每個(gè)事件目標(biāo)對(duì)于每種事件類型只能添加一個(gè)事件處理程序。
DOM2級(jí)事件處理程序
DOM2級(jí)事件處理程序定義了兩個(gè)方法用于處理指定和刪除事件處理程序的操作:addEventListener()和removeEventListener()
所有DOM節(jié)點(diǎn)中都包含這兩個(gè)方法,并且它們都接受3個(gè)參數(shù):要處理的事件名、作為事件處理程序的函數(shù)和一個(gè)布爾值。
最后的布爾值參數(shù)如果是true,表示在捕獲階段調(diào)用事件處理程序;如果是false,表示在冒泡階段調(diào)用事件處理程序。若最后的布爾值不填寫,則和false效果一樣
使用DOM2級(jí)事件處理程序的好處是可以添加多個(gè)事件處理程序,并按照他們添加的順序觸發(fā)
// 添加事件綁定
box.addEventListener("click",function(){
test('123');
},false);
function test(x){box.innerHTML += x;}
// 移除事件綁定
box.removeEventListener('click',test,false);
復(fù)制代碼
[兼容]IE9+、Firefox、Safari、Chrome和Opera支持DOM2級(jí)事件處理程序
DOM3級(jí)事件
在DOM2級(jí)事件的基礎(chǔ)上添加了更多的事件類型。
UI事件,當(dāng)用戶與頁(yè)面上的元素交互時(shí)觸發(fā),如:load、scroll
焦點(diǎn)事件,當(dāng)元素獲得或失去焦點(diǎn)時(shí)觸發(fā),如:blur、focus
鼠標(biāo)事件,當(dāng)用戶通過(guò)鼠標(biāo)在頁(yè)面執(zhí)行操作時(shí)觸發(fā)如:dblclick、mouseup
滾輪事件,當(dāng)使用鼠標(biāo)滾輪或類似設(shè)備時(shí)觸發(fā),如:mousewheel
文本事件,當(dāng)在文檔中輸入文本時(shí)觸發(fā),如:textInput
鍵盤事件,當(dāng)用戶通過(guò)鍵盤在頁(yè)面上執(zhí)行操作時(shí)觸發(fā),如:keydown、keypress
合成事件,當(dāng)為IME(輸入法編輯器)輸入字符時(shí)觸發(fā),如:compositionstart
變動(dòng)事件,當(dāng)?shù)讓覦OM結(jié)構(gòu)發(fā)生變化時(shí)觸發(fā),如:DOMsubtreeModified
同時(shí)DOM3級(jí)事件也允許使用者自定義一些事件。
IE中的DOM2級(jí)事件處理程序(<=IE8)
IE實(shí)現(xiàn)了與DOM中類似的兩個(gè)方法:attachEvent()和detachEvent()。
這兩個(gè)方法接受相同的兩個(gè)參數(shù):事件處理程序名稱與事件處理程序函數(shù)。
var btn = document.getElementById("myBtn");
var handler = function(){
alert("Clicked");
};
// 綁定事件
btn.attachEvent("onclick", handler);
// 移除事件
btn.detachEvent("onclick", handler);
復(fù)制代碼
由于IE8及更早版本只支持事件冒泡,所以通過(guò)attachEvent()添加的事件處理程序都會(huì)被添加到冒泡階段。
attachEvent()的第一個(gè)參數(shù)是"onclick",而非addEventListener()方法中的"click"。
事件處理程序會(huì)在全局作用域中運(yùn)行,因此this等于window。
兼容IE低版本事件處理程序
var EventUtil = {
addHandler: function(element, type, handler){
if (element.addEventListener){
element.addEventListener(type, handler, false);
} else if (element.attachEvent){
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
removeHandler: function(element, type, handler){
if (element.removeEventListener){
element.removeEventListener(type, handler, false);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
}
};
復(fù)制代碼
事件的 Event 對(duì)象
在觸發(fā)DOM上的某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象中包含著所有與事件有關(guān)的信息。
包括導(dǎo)致事件的元素、事件的類型以及其他與特定事件相關(guān)的信息。例如,鼠標(biāo)操作導(dǎo)致的事件對(duì)象中,會(huì)包含鼠標(biāo)位置的信息,而鍵盤操作導(dǎo)致的事件對(duì)象中,會(huì)包含與按下的鍵有關(guān)的信息。所有瀏覽器都支持event對(duì)象,但支持方式不同。
兼容DOM的瀏覽器會(huì)將一個(gè)event對(duì)象傳入到事件處理程序中。無(wú)論指定事件處理程序時(shí)使用什么方法(DOM0級(jí)或DOM2級(jí)),都會(huì)傳入event對(duì)象。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
alert(event.type); //"click"
}, false);
復(fù)制代碼
通過(guò)這個(gè)事件對(duì)象我們可以做一些事情:
阻止瀏覽器的默認(rèn)行為
阻止事件冒泡和捕獲
阻止瀏覽器的默認(rèn)行為
當(dāng)你點(diǎn)擊a標(biāo)簽時(shí),瀏覽器有一個(gè)默認(rèn)行為是跳轉(zhuǎn)鏈接。
當(dāng)我們想要自己去綁定事件,并且不讓這個(gè)默認(rèn)行為發(fā)生就叫阻止瀏覽器的默認(rèn)行為
var a = document.getElementById("a");
a.addEventListener("click", function(event){
event.preventDefault();
// 事件處理程序內(nèi)容
}, false);
復(fù)制代碼
阻止事件冒泡和捕獲
前面已經(jīng)講了事件流的行為,那么當(dāng)我們只想在目標(biāo)階段處理這個(gè)click事件,并且不希望該事件冒泡或捕獲時(shí)可以阻止事件冒泡和捕獲
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert("Clicked");
event.stopPropagation(); // 阻止事件傳播
};
// document中雖然監(jiān)聽(tīng)了點(diǎn)擊事件,由于點(diǎn)擊btn時(shí)阻止了事件傳播,因此并不會(huì)冒泡到document上
document.body.onclick = function(event){
alert("Body clicked");
};
復(fù)制代碼
事件的內(nèi)存和性能
在JavaScript中,添加到頁(yè)面上的事件處理程序數(shù)量將直接關(guān)系到頁(yè)面的整體運(yùn)行性能。導(dǎo)致這一問(wèn)題的原因是多方面的。首先,每個(gè)函數(shù)都是對(duì)象,都會(huì)占用內(nèi)存;內(nèi)存中的對(duì)象越多,性能就越差。
事件委托
對(duì)“事件處理程序過(guò)多”問(wèn)題的解決方案就是事件委托。事件委托利用了事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。例如,click事件會(huì)一直冒泡到document層次。也就是說(shuō),我們可以為整個(gè)頁(yè)面指定一個(gè)onclick事件處理程序,而不必給每個(gè)可單擊的元素分別添加事件處理程序。
- Go somewhere
- Do something
- Say hi
復(fù)制代碼
為3個(gè)li都添加相應(yīng)的點(diǎn)擊事件,這樣會(huì)導(dǎo)致性能更差,我們可以只在ul上面添加一個(gè)點(diǎn)擊事件,利用冒泡原理來(lái)處理事件
var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "click", function(event){
switch(event.target.id){
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http://www.wrox.com";
break;
case "sayHi":
alert("hi");
break;
}
});
復(fù)制代碼
事實(shí)上React中的事件處理程序都是采用的事件委托的方式。
總結(jié)
以上是生活随笔為你收集整理的html点击冒泡事件,JavaScript 浏览器事件机制(捕获、冒泡、委托)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: (3)verilog与VHDL两种语言编
- 下一篇: 网页前端设计-作业三(JavaScrip