JS15 JavaScript中的事件流

當(dāng)點擊頁面的某個按鈕時峰搪,單擊事件并不僅僅發(fā)生在按鈕上糜值。換句話說剂桥,在單機按鈕的同時忠烛,也點擊了按鈕的容器元素,甚至也單機了整個頁面权逗。

==事件流描述的是從頁面中接受時間的順序==美尸,IE和Netscape提出了幾乎相反的事件流的概念:

  1. IE的事件流是事件冒泡流
  2. Netscape是事件捕獲流
image

==大多是情況下都是將事件處理程序添加到事件冒泡階段==

事件冒泡

IE的事件流叫做事件冒泡(event bubbling),即事件開始時由最具體的元素(嵌套層次最深的節(jié)點)接受斟薇,然后逐級向上傳播师坎,直至文檔根節(jié)點。

事件冒泡是自內(nèi)而外的去觸發(fā)各個層次綁定的事件

以下面的HTML頁面為例:

<!DOCTYPE html>
<html>
  <head>
    <title>Event Bubling Example</title>
  </head>
  <body>
    <div id="myDiv">Click Me</div>
  </body>
</html>

當(dāng)點擊了<div>元素堪滨,click事件會按照如下順序傳播

graph LR
div-->body
body-->html
html-->document

事件從我們單擊的元素開始胯陋,沿著DOM樹向上傳播,在每一級節(jié)點上都會發(fā)生袱箱,直至傳播到document對象

image

事件捕獲

Netscape提出的另一種事件流是事件捕獲(event capturing)遏乔,即不太具體的節(jié)點應(yīng)該更早接收到事件,最具體的節(jié)點應(yīng)該最后接收到事件发笔。事件捕獲的用于在于事件到達(dá)預(yù)訂目標(biāo)之前捕獲它盟萨。

仍然以上面的頁面作為例子,在事件捕獲的狀態(tài)下了讨,點擊<div>元素捻激,click事件會按照相反的順序傳播:

graph LR
document-->html
html-->body
body-->div

事件捕獲過程中,document對象最先接收到click事件前计,然后事件沿DOM樹向下胞谭,直至傳播到事件的實際目標(biāo),即<div>元素

image

DOM事件流

==DOM2級事件規(guī)定的事件流包括了三個階段==:

  1. 事件捕獲階段
  2. 處于目標(biāo)階段
  3. 事件冒泡階段

首先發(fā)生的事件捕獲残炮,為截獲事件提供了機會韭赘。然后是實際的目標(biāo)接收到事件,最后一個階段是冒泡階段势就,可以在這個階段對事件作出相應(yīng)泉瞻。

以前面的HTML頁面為例,單機<div>元素會按照下圖順序觸發(fā)事件

image

在DOM事件流中苞冯,實際的目標(biāo)<div>元素在捕獲階段不會接收到事件袖牙,這意味著在捕獲階段事件從documenthtml再到body后就停止了。下一個階段是“處于目標(biāo)”階段舅锄,事件在<div>上發(fā)生鞭达。然后冒泡階段發(fā)生,事件有傳播回document

==在事件處理中,事件處于目標(biāo)階段被看成冒泡階段的一部分畴蹭。==

==為了最大限度的兼容坦仍,大多是情況下都是將事件處理程序添加到事件冒泡階段。不是特別需要叨襟,不建議在事件捕獲階段注冊事件處理程序==

DOM2級事件處理程序

DOM2級事件定義了對兩個方法:

  1. addEventListener:添加事件處理程序
  2. removeEventListener:刪除事件處理程序

所有DOM節(jié)點都包括這兩個方法繁扎,它們都接受3個參數(shù),前兩個參數(shù)是要處理的事件名和作為事件處理程序的函數(shù)糊闽。

第三個參數(shù)是一個布爾值梳玫,如果是true,表示在捕獲階段調(diào)用事件處理程序右犹,如果是false提澎,表示在冒泡階段調(diào)用事件處理程序。默認(rèn)為false

看一個例子:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>事件冒泡</title>
  <style>
    #outer {
      width: 500px;
      height: 300px;
      background: darkcyan;
    }
    #inner {
      width: 300px;
      height: 150px;
      background: chocolate;
    }
  </style>
</head>
<body>
  <div id="outer">Parent
    <div id="inner">Child</div>
  </div>
</body>
<script>
  const outer = document.getElementById('outer'),
    inner = document.getElementById('inner');
  outer.addEventListener('click', function () {
    console.log('parent')
  });
  inner.addEventListener('click', function () {
    console.log('child')
  });
</script>
</html>

image

而點擊child念链,事件在冒泡階段觸發(fā)盼忌,首先被觸發(fā)的是child本身,打印了child掂墓,然后向上層冒泡碴犬,觸發(fā)parent的事件,打印出parent

addEventListener的第三個參數(shù)改變?yōu)?code>true梆暮,則事件觸發(fā)由冒泡階段轉(zhuǎn)為捕獲階段服协,此時,當(dāng)點擊child啦粹,事件在捕獲階段的傳播從document開始偿荷,向下傳播到parent,然后再到child本身唠椭,所以會先打印parent跳纳,然后才打印出child

當(dāng)冒泡階段和捕獲階段都會調(diào)用事件處理程序時,事件按DOM事件流的順序執(zhí)行時間處理程序

image

當(dāng)事件處于目標(biāo)階段時贪嫂,事件調(diào)用順序決定于綁定事件的書寫順序寺庄。

outer.addEventListener('click', function() {
  console.log('parent冒泡')
}, false);
outer.addEventListener('click', function() {
  console.log('parent捕獲')
}, true);
inner.addEventListener('click', function() {
  console.log('child冒泡')
}, false);
inner.addEventListener('click', function() {
  console.log('child捕獲')
}, true);

上面的代碼,由于child是處于目標(biāo)階段的事件力崇,執(zhí)行的順序和我們書寫的順序有關(guān)斗塘,因此是'child冒泡'在前,而'child捕獲'在后亮靴。而parent的事件就會按照DOM事件流順序執(zhí)行馍盟,即先'parent捕獲''parent冒泡'

因此,這種情況下的調(diào)用順序是:

graph LR
parent捕獲-->child冒泡
child冒泡-->child捕獲
child捕獲-->parent冒泡

如果我們改動一下書寫順序:

outer.addEventListener('click', function() {
  console.log('parent冒泡')
}, false);
outer.addEventListener('click', function() {
  console.log('parent捕獲')
}, true);
inner.addEventListener('click', function() {
  console.log('child捕獲')
}, true);
inner.addEventListener('click', function() {
  console.log('child冒泡')
}, false);

那么調(diào)用順序就變成了:

graph LR
parent捕獲-->child捕獲
child捕獲-->child冒泡
child冒泡-->parent冒泡

事件委托

事件委托就是把一個元素響應(yīng)事件的函數(shù)委托到父層或者更外層元素上茧吊,真正綁定事件的是外層元素贞岭,當(dāng)事件響應(yīng)到需要綁定的元素上時八毯,會通過事件冒泡機制從而觸發(fā)它的外層元素的綁定事件上,然后在外層元素上去執(zhí)行函數(shù)瞄桨。

事件委托的優(yōu)點:

  1. 提高性能
  2. 動態(tài)綁定事件(不必再為動態(tài)生成的话速、需要綁定事件的元素逐一綁定事件)

這樣的一個例子:鼠標(biāo)放到li上,讓對應(yīng)的li背景變紅:

<body>
  <ul id="ul">
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
  </ul>
</body>
<script>
  document.querySelector('#ul').addEventListener('mouseover', function (e) {
    if(e.target.tagName.toLowerCase() === 'li') {
      document.querySelectorAll('#ul li').forEach(v => {
        if(v.tagName.toLowerCase() === 'li') {
          v.style.backgroundColor = '#fff'
        }
      });
      e.target.style.backgroundColor = 'red'
    }
  });
  // 上面的forEach遍歷也可以通過再添加一個事件處理程序完成
  // document.querySelector('#ul').addEventListener('mouseout', function (e) {
  //   if(e.target.tagName.toLowerCase() === 'li') {
  //     e.target.style.backgroundColor = '#fff'
  //   }
  // })
</script>
</html>

target屬性可返回事件的目標(biāo)節(jié)點(觸發(fā)該事件的節(jié)點)芯侥,如生成事件的元素尿孔、文檔或窗口。

這樣避免了在所有的li上綁定事件筹麸,而且如果是動態(tài)生成的節(jié)點词身,直接在li上綁定事件就需要再次綁定忙干,而事件委托就不需要。

利用jquery也可以實現(xiàn):

$("ul").on("click","li",function(){
  // some code here
})

其中on方法的第二個參數(shù)是一個jQuery選擇器较坛,用于指定哪些后代元素可以觸發(fā)綁定的事件留晚。如果該參數(shù)為null或被省略酵紫,則表示當(dāng)前元素自身綁定事件(實際觸發(fā)者也可能是后代元素,只要事件流能到達(dá)當(dāng)前元素即可)错维。

阻止事件冒泡

event.stopPropagation()

可以使用event.stopPropagation()來阻止事件繼續(xù)傳播奖地,在上面的parentchild的例子中,如果將child綁定的事件改為下面的代碼:

outer.addEventListener('click', function() {
  console.log('parent冒泡')
}, false);
inner.addEventListener('click', function() {
  console.log('child冒泡');
  e.stopPropagation()
}, false);

這樣再次點擊child赋焕,就不會觸發(fā)parentclick事件

e.target

也可以通過e.target來阻止事件冒泡

document.body.addEventListener('click', e = > {
  // 通過e.target判斷阻止冒泡
  if (e.target && e.target.matches('a')) {
    return;
  }
})

return false

JS的原生的事件綁定中参歹,return false是不能阻止事件冒泡的,它可以用來阻止瀏覽器的默認(rèn)事件隆判,作用于event.preventDefault()相同

在jQuery綁定的事件中使用return false犬庇,既阻止默認(rèn)行為又停止冒泡

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侨嘀,隨后出現(xiàn)的幾起案子臭挽,更是在濱河造成了極大的恐慌,老刑警劉巖咬腕,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欢峰,死亡現(xiàn)場離奇詭異,居然都是意外死亡涨共,警方通過查閱死者的電腦和手機纽帖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來举反,“玉大人抛计,你說我怎么就攤上這事≌罩” “怎么了吹截?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵瘦陈,是天一觀的道長。 經(jīng)常有香客問我波俄,道長晨逝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任懦铺,我火速辦了婚禮捉貌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冬念。我一直安慰自己趁窃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布急前。 她就那樣靜靜地躺著醒陆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裆针。 梳的紋絲不亂的頭發(fā)上刨摩,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機與錄音世吨,去河邊找鬼澡刹。 笑死,一個胖子當(dāng)著我的面吹牛耘婚,可吹牛的內(nèi)容都是我干的罢浇。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼沐祷,長吁一口氣:“原來是場噩夢啊……” “哼己莺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戈轿,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤凌受,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后思杯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胜蛉,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年色乾,在試婚紗的時候發(fā)現(xiàn)自己被綠了誊册。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡暖璧,死狀恐怖案怯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情澎办,我是刑警寧澤嘲碱,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布金砍,位于F島的核電站,受9級特大地震影響麦锯,放射性物質(zhì)發(fā)生泄漏恕稠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一扶欣、第九天 我趴在偏房一處隱蔽的房頂上張望鹅巍。 院中可真熱鬧,春花似錦料祠、人聲如沸骆捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敛苇。三九已至,卻和暖如春梧宫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背摆碉。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工塘匣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巷帝。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓忌卤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親楞泼。 傳聞我的和親對象是個殘疾皇子驰徊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的棍厂。 ??事件,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,477評論 1 11
  • js之事件機制 1超陆、事件初探 1.1 js事件的概述 JavaScript事件:JavaScript是基于事件驅(qū)動...
    道無虛閱讀 2,343評論 1 3
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5牺弹? 答:HTML5是最新的HTML標(biāo)準(zhǔn)。 注意:講述HT...
    kismetajun閱讀 27,453評論 1 45
  • 以下文章為轉(zhuǎn)載时呀,對理解JavaScript中的事件處理機制很有幫助张漂,淺顯易懂,特分享于此谨娜。 什么是事件航攒? 事件(E...
    jxyjxy閱讀 3,030評論 1 10
  • 圖片來自網(wǎng)絡(luò) 一生只想做六件事:運動漠畜,讀書币他,寫作,旅游盆驹,電影圆丹,美食。 如此過完這短暫的一生躯喇,實乃人生最大的幸事辫封。慢...
    祿指閱讀 1,185評論 6 10