博客 / 詳情

返回

爪哇學習筆記——瀏覽器事件模型

概念

事件是您在編程時系統內發生的動作或者發生的事情,系統響應事件後,如果需要,您可以某種方式對事件做出迴應。
在 Web 中, 事件在瀏覽器窗口中被觸發並且通常被綁定到窗口內部的特定部分 — 可能是一個元素、一系列元素、被加載到這個窗口的 HTML 代碼或者是整個瀏覽器窗口。

事件流

事件流描述的是頁面中接受事件的順序。

“DOM2級事件“規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。——《JavaScript高級程序設計》

根據W3C模型,事件首先被目標元素所捕獲,然後向上冒泡。——《基於MVC的JavaScript Web富應用開發》

事件捕獲

從頂層的父節點開始觸發事件,從外到內傳播,到觸發事件originTarget結束。

事件冒泡

從內層originTarget節點開始觸發事件,由內向外傳播,逐級冒泡直到頂層節點結束。

注意:不是所有的事件都支持事件冒泡的,blur、focus、load、unload、mouseenter、mouseleave以及自定義事件不支持冒泡。

阻止事件傳播

  • e.stopPropagation():大家經常聽到的可能是阻止冒泡,實際上這個方法不只能阻止冒泡,還能阻止捕獲階段的傳播。
  • e.stopImmediatePropagation():阻止監聽同一事件的其他事件監聽器被調用。如果多個事件監聽器被附加到相同元素的相同事件類型上,當此事件觸發時,它們會按其被添加的順序被調用。如果在其中一個事件監聽器中執行stopImmediatePropagation(),那麼剩下的事件監聽器都不會被調用。

阻止事件默認行為

e.preventDefault()可以阻止事件的默認行為發生,默認行為是指:點擊a標籤就轉跳到其他頁面、拖拽一個圖片到瀏覽器會自動打開、點擊表單的提交按鈕會提交表單等等,因為有的時候我們並不希望發生這些事情,所以需要阻止默認行為。

注意

  1. 只有cancelable屬性為true的事件才可以使用preventDefault()方法來取消其默認行為
  2. 既要終止冒泡又要阻止默認行為時,直接return false即可

事件處理器(事件監聽器)

用來響應事件的函數或代碼塊

事件處理程序HTML屬性(內聯事件處理程序)

屬性值就是當事件發生時要運行的JavaScript代碼

<input id="btn" type="button" onclick="handleClick(this.value)" value="hello"/>
<script>
    function handleClick(value){
        console.log(window.event);
        console.log(event);
        console.log(event.target);
        console.log('this', this); // window
        console.log(value);  // hello
        console.log(this.value);  // undefined
    }
</script>
通過這種方式指定時,會創建一個封裝着元素屬性值得函數。這個函數中有一個局部變量event(即事件對象)。通過event變量,可以直接訪問事件對象,你不用自己定義它,也不用從函數的參數列表中讀取(經測試,在chrome、和IE11中不用從函數的參數列表讀取,但是在fireFox中則需要)。在這個函數內部,this值等於事件的目標元素。——《JavaScript高級編程》

上面這段話我們可以理解為:通過html屬性指定事件處理程序時,在指定的處理函數外再包裝一層函數,然後將這個新函數賦值給btn.onclick(見DOM0級事件處理程序),這樣新函數作用於內的this就指向了事件目標元素。但是在具體的事件處理函數handler內部,由於沒有 具體的調用對象,在非嚴格模式下它內部的this指向window

DOM0級事件處理程序

var btn = document.getElementById("btn");
btn.onclick = function() {
    alert(this.id); // btn
}

DOM2級事件處理程序

主要就是addEventListenerremoveEventListener兩個方法,它們都接受3個參數:要處理的事件名、事件處理函數、一個布爾值(表示是否啓用事件捕獲),使用它們的主要好處就是可以添加多個事件處理程序。

注意:如果監聽的函數是匿名函數,沒有任何引用指向它,在不銷燬這個元素的前提下,這個監聽是無法被移除的。

IE事件處理程序(IE7、IE8)

IE實現了attachEvent()和detachEvent()。這兩個方法都接受兩個參數:事件處理程序名稱和事件處理函數。

  • 在使用這兩個函數時,事件處理程序會在全局作用域中運行,因此this指向window
  • 這些事件處理程序不是以添加它們的方式運行的,而是以相反的順序觸發

跨瀏覽器的事件處理程序

function addHandler(target, eventType, handler) {

    if (target.addEventListener) { // DOM2 Events
        target.addEventListener(eventType, handler, false);
    } else if (target.attachEvent) { // IE
        target.attachEvent('on' + eventType, handler);
    } else {
        target['on' + eventType] = handler;
    }
}

function removeHandler(target, eventType, handler) {

    if (target.removeEventListener) {
        target.removeEventListener(eventType, handler, false);
    } else if (target.detachEvent) { 
        target.detachEvent('on' + eventType, handler);
    } else {
        target['on' + eventType] = null;
    }
}

// 阻止事件 (主要是事件冒泡,因為IE不支持事件捕獲)
function stopPropagation(e) {
    if (e.stopPropagation) {
        e.stopPropagation(); // 標準w3c
    } else {
        e.cancelBubble = true; // IE
    }
}

// 取消事件的默認行為
function preventDefault(e) {
    if (e.preventDefault) {
        e.preventDefault(); // 標準w3c
    } else {
        e.returnValue = false; // IE
    }
}

事件委託

通俗來講,就是把一個元素響應事件(click、keydown等)的函數委託到另一個元素。
通常會把一個或一組元素的事件委託到它的父層或者更外層元素上,真正綁定事件的是外層元素,當事件響應到需要綁定的元素上時,會通過事件冒泡機制觸發。

自定義事件

非IE瀏覽器

  • 方式一

    // 創建事件,參數為事件類型
    var event = new Event('Event'); 
    
    // 初始化事件
    // initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
    event.initEvent('build', true, true); 
    
    elem.addEventListener('build', function(e) {
      // do something
    }, false);
    
    // 觸發事件
    elem.dispatchEvent(event);
  • 方式二
    Event的構造函數定義為new(type: string, eventInitDict?: EventInit): Event;
    其中EventInit的定義為:

    interface EventInit {
      bubbles?: boolean; // 是否冒泡,默認false
      cancelable?: boolean; // 是否可取消,默認false
      composed?: boolean; //是否是否會在shadow DOM根節點之外觸發偵聽器,默認false
    }
    // 創建及初始化事件
    var event = new Event('build', {
      bubbles: true,
      cancelable: true
    });
    
    elem.addEventListener('build', function(e) {
      // do something
    }, false);
    
    // 觸發事件
    elem.dispatchEvent(event);

IE8及之前瀏覽器

var event = document.createEventObject(); // 不接受任何參數

// 給event的屬性賦值...

elem.fireEvent('onclick', event); // 觸發事件

在調用fireEvent()方法時,會自動為event對象添加srcElementtype屬性;其他屬性則都是必須通過手工添加的。

事件對象

事件對象分為DOM中的事件對象和IE中的事件對象,應該是為了兼容,Event對象的中的屬性和方法將這兩種事件對象的屬性方法都包括進去了。

DOM中事件對象的屬性/方法

  • bubbles: boolean表明事件是否冒泡
  • cancelable: boolean表明是否可以取消事件默認行為
  • composed: boolean是否是否會在shadow DOM根節點之外觸發偵聽器
  • currentTarget: EventTarget當前正在調用事件處理函數的那個元素
  • defaultPrevented: boolean為true表示已經調用了preventDefault()方法
  • eventPhase: number調用事件處理程序的階段:1表示捕獲階段,2表示“處於目標”,3表示冒泡階段
  • isTrusted: boolean表明是否是瀏覽器生成的事件
  • target: EventTarget事件的目標
  • initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
  • preventDefault(): void
  • stopImmediatePropagation(): void
  • stopPropagation(): void
  • type: string事件類型

IE中的事件對象的屬性/方法

  • cancelBubble: boolean默認值為false,但將其設置為true就可以取消事件冒泡
  • returnValue: boolean默認值為true,但將其設置為false就可以取消事件的默認行為
  • srcElement: Element事件的目標,對應target
  • type: string事件類型(IE中的type與DOM中的type是相同的)

注意targetcurrentTarget的區別

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.