事件流
事件流出現(xiàn)的歷史背景
當(dāng)瀏覽器發(fā)展到第四代時(shí)(IE4 及 Netscape Communicator 4),瀏覽器開發(fā)團(tuán)隊(duì)遇到了一個(gè)很有意思 的問題:頁面的哪一部分會(huì)擁有某個(gè)特定的事件?要明白這個(gè)問題問的是什么,可以想象畫在一張紙上 的一組同心圓。如果你把手指放在圓心上,那么你的手指指向的不是一個(gè)圓,而是紙上的所有圓。兩家 公司的瀏覽器開發(fā)團(tuán)隊(duì)在看待瀏覽器事件方面還是一致的。如果你單擊了某個(gè)按鈕,他們都認(rèn)為單擊事 件不僅僅發(fā)生在按鈕上。換句話說,在單擊按鈕的同時(shí),你也單擊了按鈕的容器元素,甚至也單擊了整 個(gè)頁面。
事件流描述的是從頁面中接收事件的順序。
但有意思的是,IE 和 Netscape 開發(fā)團(tuán)隊(duì)居然提出了差 不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而 Netscape Communicator 的事件流是事件捕獲流。
IE的事件流叫做時(shí)間冒泡,即事件開始時(shí)由文檔中最內(nèi)層的元素接收,然后逐級(jí)往上傳播。
<!DOCTYPE html> <html> <head> <title>Event Bubbling Example</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html>
如果你單擊了頁面中的<div>元素,那么這個(gè) click 事件會(huì)按照如下順序傳播:
div->body->html->document
也就是說,click 事件首先在<div>元素上發(fā)生,而這個(gè)元素就是我們單擊的元素。然后,click 事件沿 DOM 樹向上傳播,在每一級(jí)節(jié)點(diǎn)上都會(huì)發(fā)生,直至傳播到 document 對(duì)象。圖 13-1 展示了事件 冒泡的過程。
IE事件流
所有現(xiàn)代瀏覽器都支持事件冒泡,但在具體實(shí)現(xiàn)上還是有一些差別。IE5.5 及更早版本中的事件冒 泡會(huì)跳過<html>元素(從<body>直接跳到 document)。IE9、Firefox、Chrome 和 Safari 則將事件一直 冒泡到 window 對(duì)象。
Netscape Communicator 團(tuán)隊(duì)提出的另一種事件流叫做事件捕獲(event capturing)。事件捕獲的思想是不太具體的節(jié)點(diǎn)應(yīng)該更早接收到事件,而最具體的節(jié)點(diǎn)應(yīng)該最后接收到事件。事件捕獲的用意在于在 事件到達(dá)預(yù)定目標(biāo)之前捕獲它。如果仍以前面的 HTML 頁面作為演示事件捕獲的例子,那么單擊<div> 元素就會(huì)以下列順序觸發(fā) click 事件。
document->html->body->div
在事件捕獲過程中,document 對(duì)象首先接收到 click 事件,然后事件沿 DOM 樹依次向下,一直傳播到事件的實(shí)際目標(biāo),即<div>元素 。
事件捕獲
雖然事件捕獲是 Netscape Communicator 唯一支持的事件流模型,但 IE9、Safari、Chrome、Opera 和 Firefox 目前也都支持這種事件流模型。盡管“DOM2 級(jí)事件”規(guī)范要求事件應(yīng)該從 document 對(duì)象開始傳播,但這些瀏覽器都是從 window 對(duì)象開始捕獲事件的。
由于老版本的瀏覽器不支持,因此很少有人使用事件捕獲。
“DOM2級(jí)事件”規(guī)定的事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段和事件冒泡階段。首 先發(fā)生的是事件捕獲,為截獲事件提供了機(jī)會(huì)。然后是實(shí)際的目標(biāo)接收到事件。最后一個(gè)階段是冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。以前面簡單的 HTML 頁面為例,單擊<div>元素會(huì)按照如下圖所示順序觸發(fā)事件。
DOM事件流
在 DOM 事件流中,實(shí)際的目標(biāo)(<div>元素)在捕獲階段不會(huì)接收到事件。這意味著在捕獲階段, 事件從 document 到<html>再到<body>后就停止了。下一個(gè)階段是“處于目標(biāo)”階段,于是事件在<div> 上發(fā)生,并在事件處理(后面將會(huì)討論這個(gè)概念)中被看成冒泡階段的一部分。然后,冒泡階段發(fā)生, 事件又傳播回文檔。
多數(shù)支持 DOM 事件流的瀏覽器都實(shí)現(xiàn)了一種特定的行為;即使“DOM2 級(jí)事件”規(guī)范明確要求捕 獲階段不會(huì)涉及事件目標(biāo),但 IE9、Safari、Chrome、Firefox 和 Opera 9.5 及更高版本都會(huì)在捕獲階段觸 發(fā)事件對(duì)象上的事件。結(jié)果,就是有兩個(gè)機(jī)會(huì)在目標(biāo)對(duì)象上面操作事件。
之后我們講解事件處理順序。
文章參考《JavaScript高級(jí)程序設(shè)計(jì)》(第三版)。
件流:
事件冒泡
取消冒泡:oEvent.cancelBubble=true
<html>
<head>
<meta charset="utf-8">
<title>無標(biāo)題文檔</title>
<style>
#div1 {width:400px; height:300px; background:#CCC; display:none;}
</style>
<script>
window.onload=function ()
{
var oBtn=document.getElementById('btn1');
var oDiv=document.getElementById('div1');
oBtn.onclick=function (ev)
{
var oEvent=ev||event;
oDiv.style.display='block';
//alert('按鈕被點(diǎn)擊了');
oEvent.cancelBubble=true; //取消事件冒泡,是解決許多問題的方法和手段
};
document.onclick=function ()
{
oDiv.style.display='none';
//alert('document被點(diǎn)擊了');
};
};
</script>
</head>
<body>
<input id="btn1" type="button" value="顯示" />
<div id="div1">
</div>
</body>
</html>
冒泡型事件:
<html>
<head>
<title>冒泡型事件</title>
<script language="javascript">
function add(sText){
var oDiv=document.getElementById("display");
oDiv.innerHTML +=sText; //輸出點(diǎn)擊順序
}
</script>
</head>
<body onclick="add('body<br>');">
<div onclick="add('div<br>');">
<p onclick="add('p<br>');">Click Me</p>
</div>
<div id="display"></div>
</body>
</html>
執(zhí)行順序:p對(duì)象 -> div對(duì)象 -> body對(duì)象
冒泡型事件執(zhí)行順序::由內(nèi)到外(p -> div -> body -> document)
注意: DOM 0級(jí)只有冒泡, 沒有捕獲
捕獲型事件
相對(duì)于IE使用冒泡型事件, Netscape使用了另一種稱為捕獲型事件(eventcapturing)的解決方案;
addEventListener(事件名稱,函數(shù), bCapture)
removeEventListener(事件名稱, 函數(shù), bCapture)
事件監(jiān)聽函數(shù)第三個(gè)參數(shù)bCapture確定是冒泡型還是捕獲型事件(true:捕獲 false:冒泡,默認(rèn)值false)
<!DOCTYPE html>
<html>
<head>
<style>
div {
background-color: coral;
border: 1px solid;
padding: 50px;
}
</style>
</head>
<body>
<div id="myDiv2">
<p id="myP2">點(diǎn)擊該段落, 我是捕獲</p>
</div>
<script>
document.getElementById("myP2").addEventListener("click", function() {
alert("你點(diǎn)擊了 P 元素!");
}, true);
document.getElementById("myDiv2").addEventListener("click", function() {
alert("你點(diǎn)擊了 DIV 元素!");
}, true);
</script>
</body>
</html>
執(zhí)行順序: div對(duì)象 -> p對(duì)象
捕獲型事件執(zhí)行順序:由外到內(nèi)(如:document -> body -> div -> p)
冒泡型:事件從內(nèi)部往外部依次執(zhí)行。
捕捉型:事件從外部往內(nèi)部依次執(zhí)行。
事件監(jiān)聽
通用監(jiān)聽方法:
1.直接在HTML標(biāo)簽中分配事件處理函數(shù):
<script language="javascript">
function add(sText){
var oDiv=document.getElementById("display");
oDiv.innerHTML +=sText; //輸出點(diǎn)擊順序
}
</script>
</head>
<body onclick="add('body<br>');">
<div onclick="add('div<br>');">
<p onclick="add('p<br>');">Click Me</p>
</div>
<div id="display"></div>
</body>
2.結(jié)構(gòu)與行為的分離:
<html>
<head>
<title>監(jiān)聽函數(shù)</title>
<script language="javascript">
window.onload=function(){
var oP=document.getElementById("myP"); //找到對(duì)象
oP.onclick=function(){ //設(shè)置事件監(jiān)聽函數(shù)
alert('我被點(diǎn)擊了');
}
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
事件監(jiān)聽的作用:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>無標(biāo)題文檔</title>
<script>
window.onload=function ()
{
alert('a');
};
window.onload=function ()
{
alert('b');
};
</script>
</head>
<body>
</body>
</html>
以上程序只彈出alert('b');
理解:對(duì)同一對(duì)象執(zhí)行兩次事件(同一事件類型)處理函數(shù)時(shí),往往只能只執(zhí)行后一個(gè)
因此如果要讓腳本在本瀏覽器中正常運(yùn)行的話,就必須使用瀏覽器所支持的事件監(jiān)聽器
IE中的監(jiān)聽方法:
attachEvent(事件名稱, 函數(shù)), 綁定事件處理函數(shù) attach: 貼上, 附著
detachEvent(事件名稱, 函數(shù)), 解除綁定 detach: 分離, 拆開
注意:IE只支持冒泡型事件監(jiān)聽, 沒有第三個(gè)參數(shù), 事件名稱前要加"on";
<html>
<head>
<title>多個(gè)監(jiān)聽函數(shù)</title>
<script language="javascript">
function fnClick1(){
alert("我被fnClick1點(diǎn)擊了");
}
function fnClick2(){
alert("我被fnClick2點(diǎn)擊了");
//oP.detachEvent("onclick",fnClick1); //刪除監(jiān)聽函數(shù)1
}
var oP;
window.onload=function(){
oP=document.getElementById("myP"); //找到對(duì)象
oP.attachEvent("onclick",fnClick1); //添加監(jiān)聽函數(shù)1
oP.attachEvent("onclick",fnClick2); //添加監(jiān)聽函數(shù)2
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
標(biāo)準(zhǔn)DOM的事件監(jiān)聽:
addEventListener(事件名稱,函數(shù), 捕獲)
element.addEventListener(event, function, useCapture)
removeEventListener(事件名稱, 函數(shù), 捕獲)
element.removeEventListener(event, function, useCapture)
bCapture 是用于冒泡階段還是捕獲階段(true:捕獲 false:冒泡,默認(rèn)值false)
注意:IE9版本已支持
<html>
<head>
<title>標(biāo)準(zhǔn)DOM的事件監(jiān)聽</title>
<script language="javascript">
function fnClick1(){
alert("我被fnClick1點(diǎn)擊了");
//oP.removeEventListener("click",fnClick2,false); //刪除監(jiān)聽函數(shù)2
}
function fnClick2(){
alert("我被fnClick2點(diǎn)擊了");
}
var oP;
window.onload=function(){
oP=document.getElementById("myP"); //找到對(duì)象
oP.addEventListener("click",fnClick1,false); //添加監(jiān)聽函數(shù)1
oP.addEventListener("click",fnClick2,false); //添加監(jiān)聽函數(shù)2
}
</script>
</head>
<body>
<div>
<p id="myP">Click Me</p>
</div>
</body>
</html>
編寫兼容性事件監(jiān)聽函數(shù)
function addEvent(obj, ev, fn){
if(obj.addEventListener){
obj.addEventListener(ev, fn, false);
}else{
obj.attachEvent('on'+ev, fn);
}
}
function removeEvent(obj, ev, fn){
if(obj.removeEventListener){
obj.addEventListener(ev, fn, false);
}else{
obj.attachEvent('on'+ev, fn);
}
}
實(shí)例:
#JavaScript#
事件流就是多個(gè)節(jié)點(diǎn)對(duì)象對(duì)同一個(gè)事件進(jìn)行響應(yīng)的先后順序。
1.冒泡型
事件從上向下進(jìn)行響應(yīng),稱冒泡
實(shí)例1.含五層嵌套的Div標(biāo)簽,當(dāng)點(diǎn)擊Div事件時(shí),對(duì)象邊框邊紅色,并抓取標(biāo)簽名。
代碼演示
瀏覽結(jié)果
點(diǎn)擊click事件,從內(nèi)到外一次響應(yīng)。
完整代碼
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。