最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解JS瀏覽器事件模型

 更新時間:2021年05月08日 08:44:24   作者:淺笑·  
這篇文章主要介紹了JS瀏覽器事件模型,對時間模型感興趣的同學(xué),可以參考下

什么是事件

我想你很可能聽說過事件驅(qū)動, 但是事件驅(qū)動到底是什么?為什么說瀏覽器是事件驅(qū)動的呢?

事件驅(qū)動通俗地來說就是什么都抽象為事件。

  • 一次點擊是一個事件
  • 鍵盤按下是一個事件
  • 一個網(wǎng)絡(luò)請求成功是一個事件
  • 頁面加載是一個事件
  • 頁面報錯是一個事件

瀏覽器依靠事件來驅(qū)動APP運行下去,如果沒有了事件驅(qū)動,那么APP會直接從頭到尾運行完,然后結(jié)束,事件驅(qū)動是瀏覽器的基石。

一個簡單的例子

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

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

html代碼:

<button>Change color</button>

js代碼:

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

btn.onclick = function() {
  console.log('button clicked')
}

代碼很簡單,我們在button上注冊了一個事件,這個事件的handler是一個我們定義的匿名函數(shù)。當用戶點擊了這個被注冊了事件的button的時候,這個我們定義好的匿名函數(shù)就會被執(zhí)行。

如何綁定事件

我們有三種方法可以綁定事件,分別是行內(nèi)綁定,直接賦值,用addEventListener。

內(nèi)聯(lián)這個方法非常不推薦

html代碼:

<button onclick="handleClick()">Press me</button>

然后在script標簽內(nèi)寫:

function handleClick() {
  console.log('button clicked')
}

直接賦值

和我上面舉的例子一樣:

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

btn.onclick = function() {
  console.log('button clicked')
}

這種方法有兩個缺點

不能添加多個同類型的handler

btn.onclick = functionA;
btn.onclick = functionB;

這樣只有functionB有效,這可以通過addEventListener來解決。

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

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

addEventListener

舊版本的addEventListener第三個參數(shù)是bool,新版版的第三個參數(shù)是對象,這樣方便之后的擴展,承載更多的功能, 我們來重點介紹一下它。

addEventListener可以給Element,Document,Window,甚至XMLHttpRequest等綁定事件,當指定的事件發(fā)生的時候,綁定的回調(diào)函數(shù)就會被以某種機制進行執(zhí)行,這種機制我們稍后就會講到。

語法:

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);
target.addEventListener(type, listener[, useCapture, wantsUntrusted  ]); // Gecko/Mozilla only

type是你想要綁定的事件類型,常見的有click, scroll, touch, mouseover等,舊版本的第三個參數(shù)是bool,表示是否是捕獲階段,默認是false,即默認為冒泡階段。新版本是一個對象,其中有capture(和上面功能一樣),passive和once。 once用來執(zhí)行是否只執(zhí)行一次,passive如果被指定為true表示永遠不會執(zhí)行preventDefault(),這在實現(xiàn)絲滑柔順的滾動的效果中很重要。更多請參考Improving scrolling performance with passive listeners

框架中的事件

實際上,我們現(xiàn)在大多數(shù)情況都是用框架來寫代碼,因此上面的情況其實在現(xiàn)實中是非常少見的,我們更多看到的是框架封裝好的事件,比如react的合成事件,感興趣的可以看下這幾篇文章。

  • React SyntheticEvent
  • Vue和React的優(yōu)點分別是什么?兩者的最核心差異對比是什么?

雖然我們很少時候會接觸到原生的事件,但是了解一下事件對象,事件機制,事件代理等還是很有必要的,因為框架的事件系統(tǒng)至少在這方面還是一致的,這些內(nèi)容我們接下來就會講到。

事件對象

所有的事件處理函數(shù)在被瀏覽器執(zhí)行的時候都會帶上一個事件對象,舉個例子:

function handleClick(e) {
  console.log(e);
}  

btn.addEventListener('click', handleClick);

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

屬性

  • target
  • x, y等位置信息
  • timeStamp
  • eventPhase

方法

  • preventDefault 用于阻止瀏覽器的默認行為,比如a標簽會默認進行跳轉(zhuǎn),form會默認校驗并發(fā)送請求到action指定的地址等
  • stopPropagation 用于阻止事件的繼續(xù)冒泡行為,后面講事件傳播的時候會提到。

事件傳播

前面講到了事件默認是綁定到冒泡階段的,如果你顯式令useCapture為true,則會綁定到捕獲階段。

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

一個Element上綁定的事件觸發(fā)了,那么其實會經(jīng)過三個階段。

第一個階段 - 捕獲階段

從最外層即HTML標簽開始,檢查當前元素有沒有綁定對應(yīng)捕獲階段事件,如果有則執(zhí)行,沒有則繼續(xù)往里面?zhèn)鞑?,這個過程遞歸執(zhí)行直到觸達觸發(fā)這個事件的元素為止。

偽代碼:

function capture(e, currentElement) {
    if (currentElement.listners[e.type] !== void 0) {
        currentElement.listners[e.type].forEach(fn => fn(e))
    }


    // pass down
    if (currentElement !== e.target) {
        // getActiveChild用于獲取當前事件傳播鏈路上的子節(jié)點
        capture(e, getActiveChild(currentElement, e))
    } else {
        bubble(e, currentElement)
    }
}

// 這個Event對象由引擎創(chuàng)建
capture(new Event(), document.querySelector('html'))

第二個階段 - 目標階段

上面已經(jīng)提到了,這里省略了。

第三個階段 - 冒泡階段

從觸發(fā)這個事件的元素開始,檢查當前元素有沒有綁定對應(yīng)冒泡階段事件,如果有則執(zhí)行,沒有則繼續(xù)往里面?zhèn)鞑ィ@個過程遞歸執(zhí)行直到觸達HTML為止。

偽代碼:

function bubble(e, currentElement) {
    if (currentElement.listners[e.type] !== void 0) {
        currentElement.listners[e.type].forEach(fn => fn(e))
    }
    // returning
    if (currentElement !== document.querySelector('html')) {
        bubble(e, currentElement.parent)
    }
}

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

如果你不希望事件繼續(xù)冒泡,可以用之前我提到的stopPropagation。

偽代碼:

function bubble(e, currentElement) {
    let stopped = false;
    function cb() {
        stopped = true;
    }
    if (currentElement.listners[e.type] !== void 0) {
        currentElement.listners[e.type].forEach(fn => {
            fn({
                ...e,
                stopPropagation: cb
            });
            if (stopped) return;
        })
    }
    // returning
    if (currentElement !== document.querySelector('html')) {
        bubble(e, currentElement.parent)
    }
}

事件代理

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

我們有一個如下的列表,我們想在點擊對應(yīng)列表項的時候,輸出是點擊了哪個元素。

HTML代碼:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

JS代碼:

document.querySelector('ul').addEventListener('click', e => console.log(e.target.innerHTML))

在線地址,上面說了addEventListener會默認綁定到冒泡階段,因此事件會從目標階段開始,向外層冒泡,到我們綁定了事件的ul上,ul中通過事件對象的target屬性就能獲取到是哪一個元素觸發(fā)的。

“事件會從目標階段開始”,并不是說事件沒有捕獲階段,而是我們沒有綁定捕獲階段,我描述給省略了。

我們只給外層的ul綁定了事件處理函數(shù),但是可以看到li點擊的時候,實際上會打印出對應(yīng)li的內(nèi)容(1,2,3或者4)。 我們無須給每一個li綁定事件處理函數(shù),不僅從代碼量還是性能上都有一定程度的提升。

這個有趣的東西,我們給了它一個好聽的名字“事件代理”。在實際業(yè)務(wù)中我們會經(jīng)常使用到這個技巧,這同時也是面試的高頻考點。

總結(jié)

事件其實不是瀏覽器特有的,和JS語言也沒有什么關(guān)系,這也是我為什么沒有將其劃分到JS部分的原因。很多地方都有事件系統(tǒng),但是各種事件模型又不太一致。

我們今天講的是瀏覽器的事件模型,瀏覽器基于事件驅(qū)動,將很多東西都抽象為事件,比如用戶交互,網(wǎng)絡(luò)請求,頁面加載,報錯等,可以說事件是瀏覽器正常運行的基石。

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

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

事件對象有很多屬性和方法,允許你在事件處理函數(shù)中進行讀取和操作,比如讀取點擊的坐標信息,阻止冒泡等。

最后我們通過一個例子,說明了如何利用冒泡機制來實現(xiàn)事件代理。

以上就是詳解JS瀏覽器事件模型的詳細內(nèi)容,更多關(guān)于JS瀏覽器事件模型的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • js排序與重組的實例講解

    js排序與重組的實例講解

    下面小編就為大家?guī)硪黄猨s排序與重組的實例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Bootstrap 樹控件使用經(jīng)驗分享(圖文解說)

    Bootstrap 樹控件使用經(jīng)驗分享(圖文解說)

    很多項目中使用樹來展示層級關(guān)系,還有些樹是為了選中項然后其他地方調(diào)用選中項。今天腳本之家小編給大家?guī)砹薆ootstrap 樹控件使用經(jīng)驗分享,需要的朋友參考下吧
    2017-11-11
  • 基于javascript實現(xiàn)樣式清新圖片輪播特效

    基于javascript實現(xiàn)樣式清新圖片輪播特效

    這篇文章主要為大家詳細介紹了基于javascript實現(xiàn)樣式清新圖片輪播特效,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 解決LayUI加上form.render()下拉框和單選以及復(fù)選框不出來的問題

    解決LayUI加上form.render()下拉框和單選以及復(fù)選框不出來的問題

    今天小編就為大家分享一篇解決LayUI加上form.render()下拉框和單選以及復(fù)選框不出來的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • json前后端數(shù)據(jù)交互相關(guān)代碼

    json前后端數(shù)據(jù)交互相關(guān)代碼

    本篇文章給大家分享了關(guān)于json前后端數(shù)據(jù)交互方法實現(xiàn)的相關(guān)知識點內(nèi)容,有興趣的讀者們可以參考學(xué)習(xí)下。
    2018-09-09
  • JavaScript 序列化對象實現(xiàn)代碼

    JavaScript 序列化對象實現(xiàn)代碼

    JavaScript序列化對象實現(xiàn)代碼,需要的朋友可以參考下。
    2009-12-12
  • HTML頁面,測試JS對C函數(shù)的調(diào)用簡單實例

    HTML頁面,測試JS對C函數(shù)的調(diào)用簡單實例

    下面小編就為大家?guī)硪黄狧TML頁面,測試JS對C函數(shù)的調(diào)用簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • JS閉包原理及其使用場景解析

    JS閉包原理及其使用場景解析

    這篇文章主要介紹了JS閉包原理及其使用場景解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • 原生JS實現(xiàn)簡單放大鏡效果

    原生JS實現(xiàn)簡單放大鏡效果

    這篇文章主要為大家詳細介紹了原生JS實現(xiàn)簡單放大鏡效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • element el-input 刪除邊框的實現(xiàn)

    element el-input 刪除邊框的實現(xiàn)

    本文主要介紹了element el-input 刪除邊框的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04

最新評論

章丘市| 宁城县| 屯门区| 永顺县| 周至县| 麻栗坡县| 墨脱县| 广水市| 渝中区| 江油市| 金山区| 资源县| 确山县| 信宜市| 简阳市| 都江堰市| 灌云县| 西峡县| 潢川县| 江孜县| 博湖县| 宣恩县| 浦城县| 景德镇市| 成都市| 明溪县| 邵武市| 新田县| 广昌县| 西和县| 壤塘县| 安平县| 拉萨市| 万安县| 万州区| 阿尔山市| 夏津县| 关岭| 洪湖市| 林甸县| 电白县|