JavaScript事件是指什麼

2022-01-19 16:00:12

JavaScript事件是指在檔案或者瀏覽器中發生的一些特定互動瞬間,比如開啟某一個網頁,瀏覽器載入完成後會觸發load事件,當滑鼠懸浮於某一個元素上時會觸發hover事件,當滑鼠點選某一個元素時會觸發click事件等等。

本文操作環境:Windows7系統、javascript1.8.5版、DELL G3電腦

JavaScript事件是指什麼?

JavaScript 事件詳解

JavaScript 事件解讀

1. 事件基本概念

事件是指在檔案或者瀏覽器中發生的一些特定互動瞬間,比如開啟某一個網頁,瀏覽器載入完成後會觸發 load 事件,當滑鼠懸浮於某一個元素上時會觸發 hover 事件,當滑鼠點選某一個元素時會觸發 click 事件等等。

事件處理就是當事件被觸發後,瀏覽器響應這個事件的行為,而這個行為所對應的程式碼即為事件處理程式

2. 事件操作:監聽與移除監聽

2.1 監聽事件

瀏覽器會根據一些事件作出相對應的事件處理,事件處理的前提是需要監聽事件,監聽事件的方法主要有以下三種:

2.1.1 HTML 內聯屬性

即在 HTML 元素裡直接填寫與事件相關的屬性,屬性值為事件處理程式。範例如下:

<button onclick="console.log('You clicked me!');"></button>

onclick 對應著 click 事件,所以當按鈕被點選後,便會執行事件處理程式,即控制檯輸出 You clicked me!

不過我們需要指出的是,這種方式將 HTML 程式碼與 JavaScript 程式碼耦合在一起,不利於程式碼的維護,所以應該儘量避免使用這樣的方式。

2.1.2 DOM 屬性繫結

通過直接設定某個 DOM 節點的屬性來指定事件和事件處理程式,上程式碼:

const btn = document.getElementById("btn");
btn.onclick = function(e) {
    console.log("You clicked me!");
};

上面範例中,首先獲得 btn 這個物件,通過給這個物件新增 onclick 屬性的方式來監聽 click 事件,這個屬性值對應的就是事件處理程式。這段程式也被稱作 DOM 0 級事件處理程式。

2.1.3 事件監聽函數

標準的事件監聽函數如下:

const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
    console.log("You clicked me!");
}, false);

上面的範例表示先獲得表示節點的 btn 物件,然後在這個物件上面新增了一個事件監聽器,當監聽到 click 事件發生時,則呼叫回撥函數,即在控制檯輸出 You clicked me!addEventListener 函數包含了三個引數 false,第三個引數的含義在後面的事件觸發三個階段之後再講解。這段程式也被稱作 DOM 2 級事件處理程式。IE9+、FireFox、Safari、Chrome 和 Opera 都是支援 DOM 2 級事件處理程式的,對於 IE8 及以下版本,則用 attacEvent() 函數繫結事件。

所以我們可以寫一段具有相容性的程式碼:

function addEventHandler(obj, eventName, handler) {
    if (document.addEventListener) {
        obj.addEventListener(eventName, handler, false);
    }
    else if (document.attachEvent) {
        obj.attachEvent("on" + eventName, handler);
    }    
    else {
        obj["on" + eventName] = handler;
    }
}

2.2 移除事件監聽

在為某個元素繫結了一個事件後,如果想接觸繫結,則需要用到 removeEventListener 方法。看如下例子:

const handler = function() {
    // handler logic
}
const btn = document.getElementById("btn");

btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);

需要注意的是,繫結事件的回撥函數不能是匿名函數,必須是一個已經被宣告的函數,因為解除事件繫結時需要傳遞這個回撥函數的參照。

同樣,IE8 及以下版本也不支援上面的方法,而是用 detachEvent 代替。

const handler = function() {
    // handler logic
}
const btn = document.getElementById("btn");

btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler);

同樣,可以寫一段具有相容性的刪除事件函數:

function removeEventHandler(obj, eventName, handler) {
    if (document.removeEventListener) {
        obj.removeEventListener(eventName, handler, false);
    }
    else if (document.detachEvent) {
        obj,detachEvent("on" + eventName, handler);
    }
    else {
        obj["on" + eventName] = null;
    }
}

3. 事件觸發過程

事件流描述了頁面接收事件的順序。現代瀏覽器(指 IE6-IE8 除外的瀏覽器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事件流包含三個過程,分別是捕獲階段、目標階段和冒泡階段,下圖形象地說明這個過程:

5c7d5af4ceb4266b75f8ee93450ce87.png

下面就詳細地講解這三個過程。

3.1 捕獲階段

當我們對 DOM 元素進行操作時,比如滑鼠點選、懸浮等,就會有一個事件傳輸到這個 DOM 元素,這個事件從 Window 開始,依次經過 docuemnt、html、body,再不斷經過子節點直到到達目標元素,從 Window 到達目標元素父節點的過程稱為捕獲階段,注意此時還未到達目標節點。

3.2 目標階段

捕獲階段結束時,事件到達了目標節點的父節點,最終到達目標節點,並在目標節點上觸發了這個事件,這就是目標階段

需要注意的是,事件觸發的目標節點為最底層的節點。比如下面的例子:

<div>
    <p>你猜,目標在這裡還是<span>那裡</span>。</p>
</div>

當我們點選「那裡」的時候,目標節點是<span></span>,點選「這裡」的時候,目標節點是<p></p>,而當我們點選<p></p>區域之外,<p></p>區域之內時,目標節點就是<p></p>

3.3 冒泡階段

當事件到達目標節點之後,就會沿著原路返回,這個過程有點類似水泡從水底浮出水面的過程,所以稱這個過程為冒泡階段

針對這個過程,wilsonpage 做了一個 DEMO,可以非常直觀地檢視這個過程。

現在再看 addEventListener(eventName, handler, useCapture) 函數。第三個引數是 useCapture,代表是否在捕獲階段進行事件處理, 如果是 false, 則在冒泡階段進行事件處理,如果是 true,在捕獲階段進行事件處理,預設是 false。這麼設計的主要原因是當年微軟和 netscape 之間的瀏覽器戰爭打得火熱,netscape 主張捕獲方式,微軟主張冒泡方式,W3C 採用了折中的方式,即先捕獲再冒泡。

4、事件委託

上面我們講了事件的冒泡機制,我們可以利用這一特性來提高頁面效能,事件委託便事件冒泡是最典型的應用之一。

何謂「委託」?在現實中,當我們不想做某件事時,便「委託」給他人,讓他人代為完成。JavaScript 中,事件的委託表示給元素的父級或者祖級,甚至頁面,由他們來繫結事件,然後利用事件冒泡的基本原理,通過事件目標物件進行檢測,然後執行相關操作。看下面例子:

// HTML
<ul id="list">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
</ul>

// JavaScript
var list = document.getElementById("list");
list.addEventListener("click", function(e) {
    console.log(e.target);
});

上面的例子中,5 個列表項的點選事件均委託給了父元素 <ul id="list"></ul>

先看看事件委託的可行性。有人會問,當事件不是加在某個元素上的,如何在這個元素上觸發事件呢?我們就是利用事件冒泡的機制,事件流到達目標元素後會向上冒泡,此時父元素接收到事件流便會執行事件執行程式。有人又會問,被委託的父元素下面如果有很多子元素,怎麼知道事件流來自於哪個子元素呢?這個我們可以從事件物件中的 target 屬性獲得。事件物件下面會詳細講解。

我們再來看看為什麼需要事件委託。

  • 減少事件繫結。上面的例子中,也可以分別給每個列表項繫結事件,但利用事件委託的方式不僅省去了一一系結的麻煩,也提升了網頁的效能,因為每繫結一個事件便會增加記憶體使用。

  • 可以動態監聽繫結。上面的例子中,我們對 5 個列表項進行了事件監聽,當刪除一個列表項時不需要單獨刪除這個列表項所繫結的事件,而增加一個列表項時也不需要單獨為新增項繫結事件。

看了上面的例子和解釋,我們可以看出事件委託的核心就是監聽一個 DOM 中更高層、更不具體的元素,等到事件冒泡到這個不具體元素時,通過 event 物件的 target 屬性來獲取觸發事件的具體元素。

5、阻止事件冒泡

事件委託是事件冒泡的一個應用,但有時候我們並不希望事件冒泡。比如下面的例子:

const ele = document.getElementById("ele");
ele.addEventListener("click", function() {
    console.log("ele-click");
}, false);

document.addEventListener("click", function() {
    console.log("document-click");
}, false);

我們本意是當點選 ele 元素區域時顯示 "ele-click",點選其他區域時顯示 "document-click"。但是我們發現點選 ele 元素區域時會依次顯示 "ele-click" "document-click"。那是因為繫結在 ele 上的事件冒泡到了 document 上。想要解決這個問題,只需要加一行程式碼:

const ele = document.getElementById("ele");
ele.addEventListener("click", function(e) {
    console.log("ele-click");
    e.stopPropagation(); // 阻止事件冒泡
}, false);

document.addEventListener("click", function(e) {
    console.log("document-click");
}, false);

我們還能用 e.cancelBubble = true 來替代 e.stopPropagation()。網上的說法是 cancelBubble 僅僅適用於 IE,而 stopPropagation 適用於其他瀏覽器。但根據我實驗的結果,現代瀏覽器(IE9 及 以上、Chrome、FF 等)均同時支援這兩種寫法。為了保險起見,我們可以採用以下程式碼:

function preventBubble(e) {
    if (!e) {
        const e = window.event;
    }
    e.cancelBubble = true;
    if (e.stopPropagation) {
        e.stopPropagation();
    }
}

6、event 物件

Event 物件代表事件的狀態,比如事件在其中發生的元素、鍵盤按鍵的狀態、滑鼠的位置、滑鼠按鈕的狀態。當一個事件被觸發的時候,就會建立一個事件物件。

我們用下面的程式碼列印出事件物件:

<div id="list">
    <li>Item 1</li>
    <li>Item 2</li>
</div>
<script>
    var list = document.getElementById("list");
    list.addEventListener("click", function(e) {
        console.log(e);
    });
</script>

chrome 49 的執行結果如下:

f48e30a58bce9905fade7cb2d2dcf03.png

下面介紹一些比較常用的屬性和方法。

target、 srcElement、 currentTarget 和 relatedTarget、fromElement、 toElement

  • target 與 srcElement 完全相同;

  • target 指觸發事件的元素, currentTarget 指事件所繫結的元素;

  • relatedTarget: 與事件的目標節點相關的節點。對於 mouseover 事件來說,該屬性是滑鼠指標移到目標節點上時所離開的那個節點。對於 mouseout 事件來說,該屬性是離開目標時,滑鼠指標進入的節點。
    對於其他型別的事件來說,這個屬性沒有用;

  • fromElement 和 toElement 僅僅對於 mouseover 和 mouseout 事件有效。

以上面的例子說明,當點選 <li>Item 1</li> 時,target 就是 <li>Item 1</li> 元素,而 currentTarget 是 <p id="list"></p>

clientX/Y、 screenX/Y、 pageX/Y、 offsetX/Y

上圖:

  • offsetX/Y: 點選位置相對於所處元素左上角的位置;

  • clientX/Y: 點選位置相對於瀏覽器內容區域左上角的位置;

  • screenX/Y: 點選位置相對於螢幕左上角的位置;

  • pageX/Y: 點選位置相對整張頁面左上角的位置;

  • pageX/Y 與 clientX/Y 一般情況下會相同,只有出現卷軸時才不一樣。

altKey、 ctrlKey、 shiftKey

  • altKey: 返回當事件被觸發時,"ALT" 是否被按下;

  • ctrlKey: 返回當事件被觸發時,"CTRL" 鍵是否被按下;

  • shiftKey: 返回當事件被觸發時,"SHIFT" 鍵是否被按下;

其他屬性

  • type: 返回當前 Event 物件表示的事件的名稱

  • bubbles: 返回布林值,指示事件是否是起泡事件型別;

  • cancelable: 返回布林值,指示事件是否可擁可取消的預設動作;

  • eventPhase: 返回事件傳播的當前階段,有三個值: Event.CAPTURING_PHASE、 Event.AT_TARGET、 Event.BUBBLING_PHASE,對應的值為 1、2、3,分別表示捕獲階段、正常事件派發和起泡階段;

  • path: 冒泡階段經過的節點;

方法

  • preventDefault(): 通知瀏覽器不要執行與事件關聯的預設動作;

  • stopPropagation(): 阻止冒泡;

推薦學習:《》

以上就是JavaScript事件是指什麼的詳細內容,更多請關注TW511.COM其它相關文章!