瀏覽器事件模型

2022-07-11 01:57:11 字數 4553 閱讀 9059

我想你很可能聽說過事件驅動, 但是事件驅動到底是什麼?為什麼說瀏覽器是事件驅動的呢?為什麼 nodejs 也是事件驅動的 ? 兩者是一回事麼?

實際上不管是瀏覽器還是 nodejs 都是事件驅動的,都有自己的事件模型。在這裡,我們只講解瀏覽器端的事件模型,如果對 nodejs 事件模型感興趣的,請期待我的 nodejs 部分的講解。

事件驅動通俗地來說就是什麼都抽象為事件。

本篇文章不講解事件迴圈的內容,事件迴圈部分會在本章的其他章節講解,敬請期待。

其實現實中的紅綠燈就是一種事件,它告訴我們現在是紅燈狀態,綠燈狀態,還是黃燈狀態。 我們需要根據這個事件自己去完成一些操作,比如紅燈和黃燈我們需要等待,綠燈我們可以過馬路。

下面我們來看乙個最簡單的瀏覽器端的事件:

html**:

change colorbutton>

js**:

var btn = document.queryselector('button');

btn.onclick = function()

**很簡單,我們在button上註冊了乙個事件,這個事件的handler是乙個我們定義的匿名函式。當使用者點選了這個被註冊了事件的button的時候,這個我們定義好的匿名函式就會被執行。

我們有三種方法可以繫結事件,分別是行內繫結,直接賦值,用addeventlistener。

這個方法非常不推薦
html**:

press mebutton>

然後在script標籤內寫:

function handleclick() 

和我上面舉的例子一樣:

var btn = document.queryselector('button');

btn.onclick = function()

這種方法有兩個缺點

不能新增多個同型別的handler

btn.onclick = functiona;

btn.onclick = functionb;

這樣只有functionb有效,這可以通過addeventlistener來解決。

不能控制在哪個階段來執行,這個會在後面將事件捕獲/冒泡的時候講到。這個同樣可以通過addeventlistener來解決。

因此addeventlistener橫空出世,這個也是目前推薦的寫法。

舊版本的addeventlistener第三個引數是bool,新版版的第三個引數是物件,這樣方便之後的擴充套件,承載更多的功能, 我們來重點介紹一下它。

addeventlistener可以給element,document,window,甚至xmlhttprequest等繫結事件,當指定的事件發生的時候,繫結的**函式就會被以某種機制進行執行,這種機制我們稍後就會講到。

語法:

target.addeventlistener(type, listener[, options]);

target.addeventlistener(type, listener[, usecapture]);

target.addeventlistener(type, listener[, usecapture, wantsuntrusted ]); // gecko/mozilla only

type是你想要繫結的事件型別,常見的有click, scroll, touch, mouseover等,舊版本的第三個引數是bool,表示是否是捕獲階段,預設是false,即預設為冒泡階段。新版本是乙個物件,其中有capture(和上面功能一樣),passive和once。 once用來執行是否只執行一次,passive如果被指定為true表示永遠不會執行preventdefault(),這在實現絲滑柔順的滾動的效果中很重要。更多請參考improving scrolling performance with passive listeners

雖然我們很少時候會接觸到原生的事件,但是了解一下事件物件,事件機制,事件**等還是很有必要的,因為框架的事件系統至少在這方面還是一致的,這些內容我們接下來就會講到。

function handleclick(e)   

btn.addeventlistener('click', handleclick);

這個e就是事件物件,即event object。 這個物件有一些很有用的屬性和方法,下面舉幾個常用的屬性和方法。

...
前面講到了事件預設是繫結到冒泡階段的,如果你顯式令usecapture為true,則會繫結到捕獲階段。

事件捕獲很有意思,以至於我會經常出事件的題目加上一點事件傳播的機制,讓候選人進行回答,這很能體現乙個人的水平。了解事件的傳播機制,對於一些特定問題有著非常大的作用。

乙個element上繫結的事件觸發了,那麼其實會經過三個階段。

從最外層即html標籤開始,檢查當前元素有沒有繫結對應捕獲階段事件,如果有則執行,沒有則繼續往裡面傳播,這個過程遞迴執行直到觸達觸發這個事件的元素為止。

偽**:

function capture(e, currentelement) 

// pass down

if (currentelement !== e.target) else

}// 這個event物件由引擎建立

capture(new event(), document.queryselector('html'))

上面已經提到了,這裡省略了。

從觸發這個事件的元素開始,檢查當前元素有沒有繫結對應冒泡階段事件,如果有則執行,沒有則繼續往裡面傳播,這個過程遞迴執行直到觸達html為止。

偽**:

function bubble(e, currentelement) 

// returning

if (currentelement !== document.queryselector('html'))

}

上述的過程用圖來表示為:

如果你不希望事件繼續冒泡,可以用之前我提到的stoppropagation。

偽**:

function bubble(e, currentelement) 

if (currentelement.listners[e.type] !== void 0) );

if (stopped) return;})}

// returning

if (currentelement !== document.queryselector('html'))

}

利用上面提到的事件冒泡機制,我們可以選擇做一些有趣的東西。 舉個例子:

我們有乙個如下的列表,我們想在點選對應列表項的時候,輸出是點選了哪個元素。

html**:

1li>

2li>

3li>

4li>

ul>

js**:

document.queryselector('ul').addeventlistener('click', e => console.log(e.target.innerhtml))

「事件會從目標階段開始」,並不是說事件沒有捕獲階段,而是我們沒有繫結捕獲階段,我描述給省略了。
我們只給外層的ul繫結了事件處理函式,但是可以看到li點選的時候,實際上會列印出對應li的內容(1,2,3或者4)。 我們無須給每乙個li繫結事件處理函式,不僅從**量還是效能上都有一定程度的提公升。

這個有趣的東西,我們給了它乙個好聽的名字「事件**」。在實際業務中我們會經常使用到這個技巧,這同時也是面試的高頻考點。

廣州品牌設計公司

事件其實不是瀏覽器特有的,和js語言也沒有什麼關係,這也是我為什麼沒有將其劃分到js部分的原因。很多地方都有事件系統,但是各種事件模型又不太一致。

我們今天講的是瀏覽器的事件模型,瀏覽器基於事件驅動,將很多東西都抽象為事件,比如使用者互動,網路請求,頁面載入,報錯等,可以說事件是瀏覽器正常執行的基石。

我們在使用的框架都對事件進行了不同程度的封裝和處理,除了了解原生的事件和原理,有時候了解一下框架本身對事件的處理也是很有必要的。

當發生乙個事件的時候,瀏覽器會初始化乙個事件物件,然後將這個事件物件按照一定的邏輯進行傳播,這個邏輯就是事件傳播機制。 我們提到了事件傳播其實分為三個階段,按照時間先後順序分為捕獲階段,目標階段和冒泡階段。開發者可以選擇監聽不同的階段,從而達到自己想要的效果。

事件物件有很多屬性和方法,允許你在事件處理函式中進行讀取和操作,比如讀取點選的座標資訊,阻止冒泡等。

最後我們通過乙個例子,說明了如何利用冒泡機制來實現事件**。

瀏覽器事件

常用瀏覽器事件與dom事件,包括滑鼠事件 鍵盤事件 框架 物件事件 表單事件 剪貼簿事件 列印事件 拖動事件 多 事件 動畫事件 過渡事件。onbeforeinstallprompt 當使用者即將被提示安裝web應用程式時,該處理程式將在裝置上排程,其相關聯的事件可以儲存以供稍後用於在更適合的時間提...

瀏覽器事件

常用瀏覽器事件與dom事件,包括滑鼠事件 鍵盤事件 框架 物件事件 表單事件 剪貼簿事件 列印事件 拖動事件 多 事件 動畫事件 過渡事件。onbeforeinstallprompt 當使用者即將被提示安裝web應用程式時,該處理程式將在裝置上排程,其相關聯的事件可以儲存以供稍後用於在更適合的時間提...

瀏覽器事件機制

事件被觸發三階段 1.document往事件觸發處傳播,會觸發遇到註冊的捕獲事件 2.傳播到事件觸發處,觸發註冊事件 3.從事件觸發處往document傳播,遇到註冊的冒泡事件,會觸發。事件觸發機制一般會按上面的順序觸發,但也有特例,如果給乙個目標節點同時註冊冒泡事件和捕獲事件,事件觸發會按註冊的順...