DOM事件傳播機(jī)制

事件:
JavaScript 和 HTML的交互是通過事件實(shí)現(xiàn)的局待。

事件是某個(gè)行為或者觸發(fā),比如點(diǎn)擊、鼠標(biāo)移動(dòng):
  • 當(dāng)用戶點(diǎn)擊鼠標(biāo)時(shí)
  • 當(dāng)網(wǎng)頁已加載時(shí)
  • 當(dāng)圖像已加載時(shí)
  • 當(dāng)鼠標(biāo)移動(dòng)到元素上時(shí)
  • 當(dāng)用戶觸發(fā)按鍵時(shí)
事件流
  • 事件冒泡



    事件開始時(shí)由最具體的元素接受帽氓,然后逐級(jí)向上傳播到較為不具體的元素

  • 事件捕獲



    不太具體的節(jié)點(diǎn)更早接受事件,而最具體的元素最后接受事件俩块,和事件冒泡相反

  • DOM事件流



    DOM2級(jí)事件規(guī)定事件流包括三個(gè)階段黎休,事件捕獲階段,處于目標(biāo)階段玉凯,時(shí)間冒泡階段势腮,首先發(fā)生的是事件捕獲,為截取事件提供機(jī)會(huì)漫仆,然后是實(shí)際目標(biāo)接受事件捎拯,最后是冒泡階段

注:Opera、Firefox盲厌、Sarfari都支持DOM事件流署照,IE不支持事件流,只支持時(shí)間冒泡

事件傳播機(jī)制

當(dāng)一個(gè)事件發(fā)生以后狸眼,它會(huì)在不同的DOM節(jié)點(diǎn)之間傳播(propagation)藤树。這種傳播分為三個(gè)階段:


  • 第一階段:從window對(duì)象傳導(dǎo)到目標(biāo)節(jié)點(diǎn),稱為“捕獲階段”(capture phase)拓萌。
  • 第二階段:在目標(biāo)節(jié)點(diǎn)上觸發(fā),稱為“目標(biāo)階段”(target phase)升略。
  • 第三階段:從目標(biāo)節(jié)點(diǎn)傳導(dǎo)回window對(duì)象微王,稱為“冒泡階段”(bubbling phase)。
    這種三階段的傳播模型品嚣,會(huì)使得一個(gè)事件在多個(gè)節(jié)點(diǎn)上觸發(fā)炕倘。
    比如:
<div>
    <p>Click Me</p>
</div>

如果對(duì)這兩個(gè)節(jié)點(diǎn)的click事件都設(shè)定監(jiān)聽函數(shù),則click事件會(huì)被觸發(fā)四次翰撑。<div><p>節(jié)點(diǎn)的捕獲階段和冒泡階段各一次:

  1. 捕獲階段:事件從<div><p>傳播時(shí)罩旋,觸發(fā)<div>click事件;
  2. 目標(biāo)階段:事件從<div>到達(dá)<p>時(shí)眶诈,觸發(fā)<p>click事件涨醋;
  3. 目標(biāo)階段:事件離開<p>時(shí),觸發(fā)<p>click事件逝撬;
  4. 冒泡階段:事件從<p>傳回<div>時(shí)浴骂,再次觸發(fā)<div>click事件。

用戶點(diǎn)擊網(wǎng)頁的時(shí)候宪潮,瀏覽器總是假定click事件的目標(biāo)節(jié)點(diǎn)溯警,就是點(diǎn)擊位置的嵌套最深的那個(gè)節(jié)點(diǎn)趣苏。所以<p>節(jié)點(diǎn)的捕獲和冒泡階段都會(huì)顯示為target階段。

event.stopPropagation()

stopPropagation方法阻止事件在DOM中繼續(xù)傳播梯轻,即取消進(jìn)一步的事件捕獲或冒泡食磕,防止再觸發(fā)定義在別的節(jié)點(diǎn)上的監(jiān)聽函數(shù),但是不包括在當(dāng)前節(jié)點(diǎn)上新定義的事件監(jiān)聽函數(shù)喳挑。
我們可以在button的事件處理程序中調(diào)用stopPropagation()從而避免注冊(cè)在body上的事件發(fā)生彬伦。

var handler = function(e){
    alert(e.type);
    e.stopPropagation();
}
addEvent(document.body, 'click', function(){alert('Clicked body')});
var btnClick = document.getElementById('btnClick');
addEvent(btnClick, 'click', handler);
//若是注釋掉e.stopPropagation();在點(diǎn)擊button的時(shí)候蟀悦,由于事件冒泡媚朦,body的click事件也會(huì)觸發(fā),但是調(diào)用后這句后日戈,事件會(huì)停止傳播询张。

event.preventDefault()

preventDafault方法取消瀏覽器對(duì)當(dāng)前事件的默認(rèn)行為,比如點(diǎn)擊鏈接后浙炼,瀏覽器跳轉(zhuǎn)到指定頁面份氧,或者按一下空格鍵,頁面向下滾動(dòng)一段距離弯屈。該方法生效的前提是蜗帜,事件的cancelable屬性為true如果為fales,則調(diào)用該方法沒有任何效果资厉。
該方法不會(huì)阻止事件的進(jìn)一步傳播(stopPropagation方法可用于這個(gè)目的)厅缺。只要在事件的傳播過程中使用了preventDefault方法,該事件的默認(rèn)方法就不會(huì)執(zhí)行宴偿。

//html代碼為
//<input type="checkbox" id="my-checkbox"/>

var cb = document.getElementById('my-checkbox');
cb.addEventListener('click', function(e){
    e.preventDafault();
},);

上面代碼為點(diǎn)擊單選框事件湘捎,設(shè)置監(jiān)聽函數(shù),取消默認(rèn)行為窄刘。由于瀏覽器的默認(rèn)行為是選中單選框窥妇,所以這段代碼會(huì)導(dǎo)致無法選中單選框。
利用這個(gè)方法娩践,可以為文本輸入框設(shè)置校驗(yàn)條件活翩。如果用戶的輸入不符合條件,就無法將字符輸入文本框翻伺。

function checkName(e){
    if(e.charCode < 97 || e.charCode > 122){
        e.preventDafault();
    }
}
//keypress監(jiān)聽函數(shù)材泄,只能輸入小寫字母,否則輸入事件的默認(rèn)事件(寫入文本框)將本取消穆趴。

如果監(jiān)聽函數(shù)最后返回布爾值false(return false)脸爱,瀏覽器也不會(huì)觸發(fā)默認(rèn)行為,與preventDafault方法有等同效果未妹。

事件代理

由于事件會(huì)在冒泡階段向上傳播到父節(jié)點(diǎn)簿废,因此可以把子節(jié)點(diǎn)的監(jiān)聽函數(shù)統(tǒng)一處理多個(gè)子元素的事件空入。這種方法叫做事件的代理
定義:事件代理就是利用事件冒泡,只指定一個(gè)事件處理程序族檬,就可以管理某一類型的所有事件歪赢。(delegation)。

var ul = document.querySelector('ul');
ul.addEventListener('click', function(event){
    if(event.target.tagName.toLowerCase() === 'li'){
        //...
    }
})

上面代碼的click事件的監(jiān)聽函數(shù)定義在<ul>節(jié)點(diǎn)单料,但是實(shí)際上埋凯,它處理額是子節(jié)點(diǎn)<li>click事件。這樣的好處是扫尖,只要定義一個(gè)監(jiān)聽函數(shù)白对,就能處理多個(gè)子節(jié)點(diǎn)的事件,且以后再添加子節(jié)點(diǎn)换怖,監(jiān)聽函數(shù)依然有效甩恼。

寫一個(gè) Demo,演示事件傳播的過程沉颂,演示阻止傳播的效果

  • 事件傳播
//html部分
<body>
  <style>
    * {
      padding: 10px;
      margin: 0;
    }
    .box,
    .container,
    .target {
      border: 1px solid;
    }
  </style>
  <div class="box">box
      <div class="container">container
          <div class="target">
            target
          </div>
      </div>
  </div>
</body>
//js部分
  <script>
  //為了減少代碼量条摸,寫一個(gè)$函數(shù)
  function $(selector){
    return document.querySelector(selector);
  }
  
  $('.box').addEventListener('click', function(e){
      console.log('box click...in 捕獲階段');
  }, true);
  $('.container').addEventListener('click', function(e){
      console.log('container click...in 捕獲階段');
  }, true);
  $('.target').addEventListener('click',function(e){
      console.log('target click...in 捕獲階段');
  }, true);
  $('.target').addEventListener('click', function(e){
      console.log('target click...in 冒泡階段');
  }, false);
  $('.container').addEventListener('click', function(e){
      console.log('container click...in 冒泡階段');
  }, false);
  $('.box').addEventListener('click', function(e){
      console.log('box click...in 冒泡階段');
  }, false);
  </script>

點(diǎn)擊target時(shí)效果圖

事件傳播預(yù)覽地址

  • 阻止事件傳播

    點(diǎn)擊target時(shí)發(fā)現(xiàn)在container捕獲階段時(shí)事件被阻止傳播了

    阻止事件傳播預(yù)覽

實(shí)現(xiàn)一個(gè)登陸/注冊(cè)頁面

參考資料

JavaScript 標(biāo)準(zhǔn)參考教程
事件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铸屉,隨后出現(xiàn)的幾起案子钉蒲,更是在濱河造成了極大的恐慌,老刑警劉巖彻坛,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷啼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡昌屉,警方通過查閱死者的電腦和手機(jī)线梗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怠益,“玉大人,你說我怎么就攤上這事瘾婿◎呃危” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵偏陪,是天一觀的道長抢呆。 經(jīng)常有香客問我,道長笛谦,這世上最難降的妖魔是什么抱虐? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮饥脑,結(jié)果婚禮上恳邀,老公的妹妹穿的比我還像新娘懦冰。我一直安慰自己,他們只是感情好谣沸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布刷钢。 她就那樣靜靜地躺著,像睡著了一般乳附。 火紅的嫁衣襯著肌膚如雪内地。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天赋除,我揣著相機(jī)與錄音阱缓,去河邊找鬼。 笑死举农,一個(gè)胖子當(dāng)著我的面吹牛荆针,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播并蝗,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼祭犯,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了滚停?” 一聲冷哼從身側(cè)響起沃粗,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎键畴,沒想到半個(gè)月后最盅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡起惕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年涡贱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惹想。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡问词,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘀粱,到底是詐尸還是另有隱情激挪,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布锋叨,位于F島的核電站垄分,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏娃磺。R本人自食惡果不足惜薄湿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豺瘤,春花似錦吆倦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瞻赶,卻和暖如春赛糟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背砸逊。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工璧南, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人师逸。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓司倚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親篓像。 傳聞我的和親對(duì)象是個(gè)殘疾皇子动知,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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

  • 事件是一種異步編程的實(shí)現(xiàn)方式,本質(zhì)上是程序各個(gè)組成部分之間的通信员辩。DOM支持大量的事件盒粮,本節(jié)介紹DOM的事件編程。...
    周花花啊閱讀 593評(píng)論 0 3
  • 以下文章為轉(zhuǎn)載奠滑,對(duì)理解JavaScript中的事件處理機(jī)制很有幫助丹皱,淺顯易懂,特分享于此宋税。 什么是事件摊崭? 事件(E...
    jxyjxy閱讀 3,030評(píng)論 1 10
  • 導(dǎo)讀:本文是teren對(duì)DOM事件知識(shí)點(diǎn)所做的進(jìn)一步整理,整理資料主要參考DOM事件簡介和饑人谷課件杰赛,如果對(duì)DOM...
    犯迷糊的小羊閱讀 4,019評(píng)論 1 5
  • JavaScript 程序采用了異步事件驅(qū)動(dòng)編程模型呢簸。在這種程序設(shè)計(jì)風(fēng)格下,當(dāng)文檔乏屯、瀏覽器阔墩、元素或與之相關(guān)的對(duì)象發(fā)...
    劼哥stone閱讀 1,253評(píng)論 3 11
  • 斜穿馬路的時(shí)候,我置身在左轉(zhuǎn)彎車的洪流里瓶珊,和許多人們隔著鐵皮靠近和離去,仿佛感覺安全耸彪。我緬懷那些倒在血泊里的人類伞芹,...
    中習(xí)習(xí)閱讀 226評(píng)論 0 5