DOM事件傳播機(jī)制

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

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



    事件開(kāi)始時(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),稱(chēng)為“捕獲階段”(capture phase)。
  • 第二階段:在目標(biāo)節(jié)點(diǎn)上觸發(fā),稱(chēng)為“目標(biāo)階段”(target phase)。
  • 第三階段:從目標(biāo)節(jié)點(diǎn)傳導(dǎo)回window對(duì)象,稱(chēng)為“冒泡階段”(bubbling phase)。
    這種三階段的傳播模型,會(huì)使得一個(gè)事件在多個(gè)節(jié)點(diǎn)上觸發(fā)。
    比如:
<div>
    <p>Click Me</p>
</div>

如果對(duì)這兩個(gè)節(jié)點(diǎn)的click事件都設(shè)定監(jiān)聽(tīng)函數(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)階段:事件離開(kāi)<p>時(shí),觸發(fā)<p>click事件;
  4. 冒泡階段:事件從<p>傳回<div>時(shí),再次觸發(fā)<div>click事件。

用戶點(diǎn)擊網(wǎng)頁(yè)的時(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)聽(tīng)函數(shù),但是不包括在當(dāng)前節(jié)點(diǎn)上新定義的事件監(jiān)聽(tīng)函數(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)到指定頁(yè)面,或者按一下空格鍵,頁(yè)面向下滾動(dòng)一段距離。該方法生效的前提是,事件的cancelable屬性為true如果為fales,則調(diào)用該方法沒(méi)有任何效果。
該方法不會(huì)阻止事件的進(jìn)一步傳播(stopPropagation方法可用于這個(gè)目的)。只要在事件的傳播過(guò)程中使用了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)聽(tīng)函數(shù),取消默認(rèn)行為。由于瀏覽器的默認(rèn)行為是選中單選框,所以這段代碼會(huì)導(dǎo)致無(wú)法選中單選框。
利用這個(gè)方法,可以為文本輸入框設(shè)置校驗(yàn)條件。如果用戶的輸入不符合條件,就無(wú)法將字符輸入文本框。

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

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

事件代理

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

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

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

寫(xiě)一個(gè) Demo,演示事件傳播的過(guò)程,演示阻止傳播的效果

  • 事件傳播
//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>
  //為了減少代碼量,寫(xiě)一個(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è)頁(yè)面

參考資料

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容