JavaScript事件機制——記一次認真準備的技術分享

先問幾個問題,你是否能快速閃過答案?

  1. 自下而上(冒泡)事件怎麼寫,自上而下(捕獲)又是怎麼寫?
  2. 捕獲型和冒泡型同時存在,誰生效?
  3. jquery的on或bind是冒泡,仍是捕獲?
  4. 冒泡可以阻止,那捕獲可以阻止嗎?
  5. stopPropagation 和 stopImmediatePropagation的區別
  6. Event.bubbles,Event.eventPhase
  7. Event.cancelable,Event.cancelBubble,event.defaultPrevented
  8. 經常使用技巧

js事件的捕獲和冒泡圖

js事件的捕獲和冒泡

舉個例子:

點擊s2,s1分別會打印什麼?javascript

<div id="s1">s1
 <div id="s2">s2</div>
</div>
<script>
s1.addEventListener("click",function(e){ 
  console.log("s1 冒泡事件"); },false);
s2.addEventListener("click",function(e){ 
  console.log("s2 冒泡事件");},false);
s1.addEventListener("click",function(e){ 
  console.log("s1 捕獲事件");},true);
s2.addEventListener("click",function(e){
  console.log("s2 捕獲事件");},true);
</script>


//s1 捕獲事件
//s2 冒泡事件
//s2 捕獲事件
//s1 冒泡事件

複製代碼

點擊s2,click事件從document->html->body->s1->s2(捕獲前進)這裏在s1上發現了捕獲註冊事件,則輸出"s1 捕獲事件"到達s2,已經到達目的節點。html

s2上註冊了冒泡和捕獲事件,先註冊的冒泡後註冊的捕獲,則先執行冒泡,輸出"s2 冒泡事件"java

再在s2上執行後註冊的事件,即捕獲事件,輸出"s2 捕獲事件"jquery

下面進入冒泡階段,按照s2->s1->body->html->documen(冒泡前進) 在s1上發現了冒泡事件,則輸出"s1 冒泡事件"瀏覽器

jQuery的on事件是冒泡

下面貼上jquery的on事件的源碼 dom

jquery的on事件源碼

經常使用技巧

onclick -->事件冒泡,重寫onlick會覆蓋以前屬性,沒有兼容性問題函數

ele.onclik = null;   //解綁單擊事件,將onlick屬性設爲null便可

複製代碼

阻止默認事件(href=""連接,submit表單提交等)ui

  1. return false; 阻止獨享屬性(經過on這種方式)綁定的事件的默認事件spa

    ele.onclick = function() {
        ……                         //你的代碼
        return false;              //經過返回false值阻止默認事件行爲
    }
    複製代碼
    可是,在jQuery中,咱們經常使用return false來阻止瀏覽器的默認行爲,那」return false「到底作了什麼?

    當你每次調用」return false「的時候,它實際上作了3件事情:
    code

    • event.preventDefault();

    • event.stopPropagation();

    • 中止回調函數執行並當即返回。

    這3件事中用來阻止瀏覽器繼續執行默認行爲的只有preventDefault,除非你想要中止事件冒泡,不然使用return false會爲你的代碼埋下很大的隱患。

    下面貼上jQuery的源碼

    jQuery->return false 源碼

  2. event.preventDefault( ); 阻止經過 addEventListener( ) 添加的事件的默認事件

    element.addEventListener(「click」, function(e){
        var event = e || window.event;
        ……
        event.preventDefault( );      //阻止默認事件
    },false);
    
    複製代碼
  3. event.returnValue = false; 阻止經過 attachEvent( ) 添加的事件的默認事件(此事件爲ie瀏覽器特有)

    element.attachEvent(「onclick」, function(e){
        var event = e || window.event;
        ……
        event.returnValue = false;  //阻止默認事件
    },false);
    
    複製代碼

把事件綁定以及事件解綁封裝成爲一個函數,兼容瀏覽器,包括IE6及以上(雖然如今基本上都放棄了IE9如下了hhhh)

// 事件綁定
function addEvent(element, eType, handle, bol) {
    if(element.addEventListener){           //若是支持addEventListener
        element.addEventListener(eType, handle, bol);
    }else if(element.attachEvent){          //若是支持attachEvent
        element.attachEvent("on"+eType, handle);
    }else{                                  //不然使用兼容的onclick綁定
        element["on"+eType] = handle;
    }
}

// 事件解綁
function removeEvent(element, eType, handle, bol) {
    if(element.addEventListener){
        element.removeEventListener(eType, handle, bol);
    }else if(element.attachEvent){
        element.detachEvent("on"+eType, handle);
    }else{
        element["on"+eType] = null;
    }
}

複製代碼

事件中止傳播 stopPropagation 和 stopImmediatePropagation

// 事件傳播到 element 元素後,就再也不向下傳播了
element.addEventListener('click', function (event) {
  event.stopPropagation();
}, true);

// 事件冒泡到 element 元素後,就再也不向上冒泡了
element.addEventListener('click', function (event) {
  event.stopPropagation();
}, false);

複製代碼

可是,stopPropagation方法只會阻止【該元素的當前事件(冒泡或者捕獲)】的傳播,不會阻止該節點的其餘click事件的監聽函數。也就是說,不是完全取消click事件,它還能夠正常建立一個新的click事件。

element.addEventListener('click', function (event) {
  event.stopPropagation();
  console.log(1);
});

element.addEventListener('click', function(event) {
  // 會觸發
  console.log(2);
});
複製代碼

若是想要完全阻止這個事件的傳播,再也不觸發後面全部click的監聽函數,可使用stopImmediatePropagation方法。 注意:是針對該事件,好比你在click裏寫了這個方法,那【使用該方法以後】的該元素上綁定的方法將失效,可是別的mousedown,mouseover方法等仍是生效的。親測過~

element.addEventListener('click', function (event) {
  // 會觸發
  console.log(‘改方法內的能夠執行’);
  event.stopImmediatePropagation();
  // 會觸發
  console.log(1);
});

element.addEventListener('click', function(event) {
  // 不會被觸發
  console.log(2);
});

// Jquery同理
$(element).click(function() {
  // 不會觸發
  console.log(‘jquery click’)
})

$(element).hover(function() {
  // 會觸發
  console.log(‘jquery click’)
})

複製代碼

Event.bubbles,Event.eventPhase

Event.bubbles屬性返回一個布爾值,表示當前事件是否會冒泡。該屬性爲只讀屬性,通常用來了解 Event 實例是否能夠冒泡。前面說過,除非顯式聲明,Event構造函數生成的事件,默認是不冒泡的。能夠根據下面的代碼來判斷事件是否冒泡,從而執行不一樣的函數。

function goInput(e) {
  if (!e.bubbles) {
    passItOn(e);
  } else {
    doOutput(e);
  }
}
複製代碼

專門查了一下不支持冒泡的事件有:

  • UI事件(load, unload, scroll, resize)
  • 焦點事件(blur, focus)
  • 鼠標事件(mouseleave, mouseenter)

Event.eventPhase屬性返回一個整數常量,表示事件目前所處的階段。該屬性只讀。

var phase = event.eventPhase;
複製代碼

Event.eventPhase的返回值有四種可能。

  • 0,事件目前沒有發生。
  • 1,事件目前處於捕獲階段,即處於從祖先節點向目標節點的傳播過程當中。
  • 2,事件到達目標節點,即Event.target屬性指向的那個節點。
  • 3,事件處於冒泡階段,即處於從目標節點向祖先節點的反向傳播過程當中。

Event.cancelable,Event.cancelBubble,event.defaultPrevented

Event.cancelable屬性返回一個布爾值,表示事件是否能夠取消。該屬性爲只讀屬性,通常用來了解 Event 實例的特性。

大多數瀏覽器的原生事件是能夠取消的。好比,取消click事件,點擊連接將無效。可是除非顯式聲明,Event構造函數生成的事件,默認是不能夠取消的。

var evt = new Event('foo');
evt.cancelable  // false
複製代碼

當Event.cancelable屬性爲true時,調用Event.preventDefault()就能夠取消這個事件,阻止瀏覽器對該事件的默認行爲。 注意,該方法只是取消事件對當前元素的默認影響,不會阻止事件的傳播。若是要阻止傳播,可使用stopPropagation()或stopImmediatePropagation()方法。

function preventEvent(event) {
  if (event.cancelable) {
    event.preventDefault();
  } else {
    console.warn('This event couldn\'t be canceled.');
    console.dir(event);
  }
}
複製代碼

Event.cancelBubble屬性是一個布爾值,該屬性能夠自行設置。若是設爲true,至關於執行Event.stopPropagation(),能夠阻止事件的傳播。

注意:MDN裏說該特性已經從 Web 標準中刪除,雖然一些瀏覽器目前仍然支持它,但也許會在將來的某個時間中止支持,請儘可能不要使用該特性。請使用 event.stopPropagation() 方法來代替該不標準的屬性.cancelBubble-MDN

Event.defaultPrevented屬性返回一個布爾值,表示該事件是否調用過Event.preventDefault方法。該屬性只讀。

if (event.defaultPrevented) {
  console.log('該事件已經取消了');
}
複製代碼

主要參考文檔:

詳解JS中的事件機制(帶實例)

事件模型

相關文章
相關標籤/搜索