DOM Event:事件流動(dòng)(Event Flow)

該文章會(huì)講述DOM規(guī)范里的事件流動(dòng)的機(jī)制鸯两。你需要對(duì)瀏覽器事件相關(guān)的知識(shí)有最基本的了解。

考慮這么個(gè)例子:

<div>
  <button id="btn">Click Me!</button>
</div>

哪怕一個(gè)web開發(fā)的初學(xué)者都會(huì)知道脏榆,當(dāng)我們鼠標(biāo)在button上點(diǎn)擊時(shí)缘厢,會(huì)在button上觸發(fā)一個(gè)click事件。但是:

  • button是div的一個(gè)子Node;從界面上來(lái)看责语,在button里點(diǎn)擊相當(dāng)于在div里點(diǎn)擊炮障;那click事件也會(huì)觸發(fā)在div上嗎?
  • 如果click事件也觸發(fā)在div上坤候,那它們會(huì)不會(huì)共用同一個(gè)事件對(duì)象胁赢?
  • 如果click事件也觸發(fā)在div上,誰(shuí)的事件會(huì)先發(fā)生白筹?
  • click事件還會(huì)在哪些元素上面觸發(fā)智末?
  • 等等...

想解答上述問(wèn)題,我們需要理解事件(Event)一個(gè)很重要的機(jī)制:事件流動(dòng)(Event Flow)徒河。

事件流動(dòng)

DOM事件不單單只會(huì)在一個(gè)Element上觸發(fā)系馆,它還會(huì)流向其他Element。事件的流動(dòng)通常會(huì)經(jīng)歷這么三個(gè)階段:

捕獲階段 -> 目標(biāo)階段 -> 冒泡階段

"eventPhase"

“eventPhase”是“Event”下的一個(gè)屬性顽照,它指明當(dāng)前event屬于那一個(gè)階段由蘑。
“eventPhase”可能是一下其中一個(gè)值:

  • Event.NONE,0代兵,沒有事件需要處理
  • Event.CAPTURING_PHASE尼酿,1,捕獲階段
  • Event.AT_TARGET植影,2裳擎,目標(biāo)階段,事件對(duì)象到達(dá)事件目標(biāo)上
  • Event.BUBBLING_PHASE思币,3鹿响,冒泡階段

下面我們?cè)敿?xì)討論一下這三個(gè)階段。

捕獲階段(capture phase)

捕獲階段的定義如下(w3c):

The event object propagate through the target's ancestors from the defaultView to the target's parent.
事件對(duì)象在事件目標(biāo)的祖先中上到下順向傳播支救,從最頂層的defaultView到事件目標(biāo)的(直系)父元素抢野。

捕獲階段發(fā)生在整個(gè)事件流動(dòng)的開始拷淘。在這階段里事件會(huì)從父(主干)到子(分支)由上往下傳播各墨,被元素一層層地捕獲。
文章開頭的例子里面启涯,捕獲階段的click事件會(huì)依次在document贬堵、body、div上觸發(fā):

document    1
  v
body    2
  v
div    3
  v
button

一般我們沒太大需要監(jiān)聽捕獲階段的事件结洼;如果你確實(shí)希望這么做黎做,需要將addEventListener的第三個(gè)參數(shù)設(shè)置為true:

// 第三個(gè)參數(shù)設(shè)置是否為捕獲階段,默認(rèn)為false
element.addEventListener('click', function() {}, true)

目標(biāo)階段(target phase)

目標(biāo)階段的定義是(w3c):

The event object arrive at the event object's event target.
事件對(duì)象到達(dá)事件目標(biāo)松忍。

例子里面蒸殿,就是事件在button上觸發(fā)的。addEventListener可以監(jiān)聽目標(biāo)階段的事件:

element.addEventListener('click', function() {})

如果事件是不可冒泡的,那整個(gè)事件流動(dòng)會(huì)到此為止宏所,不會(huì)發(fā)生下面的冒泡階段酥艳。

冒泡階段(bubble phase)

冒泡階段的定義如下(w3c):

The event object propagates through the target's ancestors in reverse order, starting with the target's parent and ending with the defaultView.
事件對(duì)象會(huì)在事件目標(biāo)的祖先元素里反向傳播,由開始的父元素到最后的defaultView(document)爬骤。

冒泡階段發(fā)生在最后充石,這也是我們最為熟悉的一個(gè)階段。在這階段里事件會(huì)從子(分支)到父(主干)逆向傳播霞玄,看起來(lái)像是一個(gè)水里的泡泡往上冒骤铃。
例子里面,冒泡階段的click事件會(huì)依次在div坷剧、body惰爬、document上觸發(fā):

document    3
  ^
body    2
  ^
div    1
  ^
button

"bubbles"

Event下的bubbles屬性標(biāo)明該事件是否為可冒泡的。一旦該值為false惫企,則說(shuō)明 evnet不可冒泡补鼻,那其流動(dòng)也會(huì)在第二階段“目標(biāo)階段”后就終止。


總結(jié)

若一個(gè)元素(div)是目標(biāo)元素(button)的祖先雅任,那事件對(duì)象會(huì)在該元素上觸發(fā)兩次:一次是捕獲階段(1)的风范,另一次是冒泡階段(3)的。當(dāng)事件對(duì)象在事件目標(biāo)元素(button)上觸發(fā)時(shí)沪么,事件流動(dòng)進(jìn)入了目標(biāo)階段(2)硼婿。

  • 想監(jiān)聽捕獲階段的事件,可以這樣:element.addEventListener('click', cb, true)禽车,將第三個(gè)參數(shù)設(shè)置為true寇漫。
  • 想監(jiān)聽冒泡階段的事件,可以這樣:element.addEventListener('click', cb,)殉摔,不使用第三個(gè)參數(shù)或?qū)⑵湓O(shè)置為false州胳。
  • 而上述的任何一種監(jiān)聽方式都可以監(jiān)聽到目標(biāo)階段的事件。

最后逸月,你可以配合這個(gè)例子來(lái)確認(rèn)一下你的理解栓撞。

      let divElement = document.querySelector('div')
      let btnElement = document.querySelector('button')
      
      document.body.addEventListener('click', event => {
        console.log('Body Click in Bubble Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      })
      
      document.body.addEventListener('click', event => {
        console.log('Body Click in Capture Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      }, true)

      divElement.addEventListener('click', event => {
        console.log('Div Click in Bubble Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      })
      
      divElement.addEventListener('click', event => {
        console.log('Div Click in Capture Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      }, true)
      
      btnElement.addEventListener('click', event => {
        console.log('Button Click in Target Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      })
      
      btnElement.addEventListener('click', event => {
        console.log('Button Click in Target Phase.')
        console.log('Event Phase: ' + event.eventPhase)
      }, true)
Body Click in Capture Phase.
Event Phase: 1
Div Click in Capture Phase.
Event Phase: 1
Button Click in Target Phase.
Event Phase: 2
Button Click in Target Phase.
Event Phase: 2
Div Click in Bubble Phase.
Event Phase: 3
Body Click in Bubble Phase.
Event Phase: 3
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碗硬,隨后出現(xiàn)的幾起案子瓤湘,更是在濱河造成了極大的恐慌,老刑警劉巖恩尾,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弛说,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡翰意,警方通過(guò)查閱死者的電腦和手機(jī)木人,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門信柿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人醒第,你說(shuō)我怎么就攤上這事角塑。” “怎么了淘讥?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵圃伶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蒲列,道長(zhǎng)窒朋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任蝗岖,我火速辦了婚禮侥猩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抵赢。我一直安慰自己欺劳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布铅鲤。 她就那樣靜靜地躺著划提,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邢享。 梳的紋絲不亂的頭發(fā)上鹏往,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音骇塘,去河邊找鬼伊履。 笑死,一個(gè)胖子當(dāng)著我的面吹牛款违,可吹牛的內(nèi)容都是我干的唐瀑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼插爹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哄辣!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起递惋,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柔滔,失蹤者是張志新(化名)和其女友劉穎溢陪,沒想到半個(gè)月后萍虽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡形真,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年杉编,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了超全。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邓馒,死狀恐怖嘶朱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情光酣,我是刑警寧澤疏遏,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站救军,受9級(jí)特大地震影響财异,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唱遭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一戳寸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拷泽,春花似錦疫鹊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至脂矫,卻和暖如春锈拨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羹唠。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工奕枢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人佩微。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓缝彬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哺眯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谷浅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 事件是一種異步編程的實(shí)現(xiàn)方式,本質(zhì)上是程序各個(gè)組成部分之間的通信奶卓。DOM支持大量的事件一疯,本節(jié)介紹DOM的事件編程。...
    周花花啊閱讀 592評(píng)論 0 3
  • 1.JQuery 基礎(chǔ) 改變web開發(fā)人員創(chuàng)造搞交互性界面的方式夺姑。設(shè)計(jì)者無(wú)需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性墩邀。 1....
    LaBaby_閱讀 1,162評(píng)論 0 1
  • 總結(jié): 鼠標(biāo)事件 1.click與dbclick事件$ele.click()$ele.click(handler(...
    阿r阿r閱讀 1,595評(píng)論 2 10
  • 前言:之前的上傳圖片用到了event.target,但是后來(lái)仔細(xì)思考了一下盏浙,自己對(duì)event.target眉睹,thi...
    Ruby君閱讀 2,002評(píng)論 1 3
  • 今天閨蜜發(fā)來(lái)微信跟我大吐苦水荔茬,由于老公長(zhǎng)期在外地出差,她生完孩子后一直在娘家?guī)Ш⒆又窈!I现芾瞎貋?lái)后慕蔚,直接來(lái)到她娘家...
    雪山飛狐兒閱讀 391評(píng)論 0 0