前端培訓-中級階段(2) - 事件(event) 事件冒泡、捕獲 - (2019-06-20期)

前端最基礎的就是 HTML+CSS+Javascript。掌握了這三門技術就算入門,但也僅僅是入門,現在前端開發的定義已經遠遠不止這些。前端小課堂(HTML/CSS/JS),本着提升技術水平,打牢基礎知識的中心思想,我們開課啦(每週四)。

前面我們已經基本掌握常規的語法語義,以及基本的使用方法。接下來我們講深入進去了解其中內在的原理。

今天我們要講什麼?

  1. 事件機制
  2. 事件對象(Event)
  3. event loop

DOM (與事件的關係,看不看無所謂)

DOM(Document Object Model——文檔對象模型)是用來呈現以及與任意 HTML 或 XML文檔交互的 API。DOM 是載入到瀏覽器中的文檔模型,以節點樹的形式來表現文檔,每個節點代表文檔的構成部分(例如:頁面元素、字符串或註釋等等)。
DOM 是萬維網上使用最爲廣泛的 API 之一,因爲它允許運行在瀏覽器中的代碼訪問文件中的節點並與之交互。節點可以被創建,移動或修改。事件監聽器可以被添加到節點上並在給定事件發生時觸發
DOM 並不是天生就被規範好了的,它是瀏覽器開始實現JavaScript時纔出現的。這個傳統的 DOM 有時會被稱爲 DOM 0。現在, WHATWG 維護DOM現存標準。
-- MDN

既然 DOM 有版本,那麼在他的環境上事件的支持也是有版本的。文檔

DOM 事件(0 級)

body.onclick 這種定義方式的。

  1. 不可以多次監聽事件,因爲是賦值的方式,下次賦值會覆蓋。
  2. 只可以在冒泡階段觸發

DOM 事件(2 級)

addEventListener 方式定義的。

  1. 可以多次監聽,切按監聽順序執行回調(有序)
  2. 取消監聽需要同一引用的函數。舉個栗子

        // 錯誤案例,兩個方法不是同一引用,導致清除不掉
        document.addEventListener('click', function(){})
        document.removeEventListener('click', function(){})
        
        // 正確案例,同一引用,可以清除。
        function documentClick(){}
        document.addEventListener('click', documentClick)
        document.removeEventListener('click', documentClick)    
    
  3. 可以選擇觸發階段(冒泡&捕獲) capture

事件機制

標準事件:EMCAScript 標準規定事件流包含三個階段,分別爲事件捕獲階段目標階段事件冒泡階段
先存個代碼,之後的例子我們用這個例子。測試看我這裏的 DEMO

<html onclick="alert('html')">
    <body onclick="alert('body')">
        <a onclick="alert('a')">click</a>
    </body>
</html>

事件捕獲階段

捕獲階段:由外到內,觸發規律爲 html > body > a
如果想在捕獲階段就觸發,需要傳入參數 {capture: true}

事件冒泡階段

冒泡階段:由內到外,觸發規律爲 a > body > html
這個階段執行是 W3C 默認的,等價於 {capture: false}

事件執行順序

clipboard.png
圖片來源-https://www.w3.org/TR/DOM-Lev...
事件的捕獲階段 > 處於目標階段 > 事件的冒泡階段 > 事件的默認行爲
這裏爲什麼要強調這個順序呢?

  1. 因爲默認行爲是在最後面,所以我們都可以用 e.preventDefault() 來阻止。
  2. 基於上條的阻止默認事件。在移動端滑動時,阻止默認事件需要手動設置 passivefalse
    passive: Boolean,設置爲 true 時,表示 listener 永遠不會調用 preventDefault()。如果 listener 仍然調用了這個函數,客戶端將會忽略它並拋出一個控制檯警告。
  3. 我們真正單擊的元素事件觸發不在冒泡和捕獲階段,而在目標階段觸發DEMO-冒泡&捕獲階段觸發事件,可以看到,他是通過定義時的先後順序來觸發的。

事件對象(Event)

Event 對象--mdn

事件對象(屬性&方法)

key 類型 描述
bubbles boolean 是否冒泡
cancelable boolean 是否可以取消的默認動作。
currentTarget Element 返回其事件監聽器觸發該事件的元素。(this 的真實指向)
eventPhase Intenger 返回事件傳播的當前階段
target Element 返回觸發此事件的元素。(事件的目標節點)
timeStamp Date 觸發的時間戳
type String 事件名稱。
isTrusted boolean 該事件是否是瀏覽器生成(true 代表是,false 代表是開發人員創建
preventDefault Function 取消事件的默認行爲在 cancelable=true 時有效
stopPropagation Function 取消事件的捕獲或者冒泡行爲在 bubbles=true 時有效
  1. IE: event.cancelBubble=true; //阻止事件冒泡
  2. IE: event.returnValue=false; //阻止事件的默認行爲
  3. 獲取事件 window.event

事件類型(分類、Event對象之類)

DOM event 子類,根據不同的事件類型,返回的對象會有些許不同,比如 Mouse 類型的,就會有單擊座標之類的。 KeyboardEvent 之類的就會有按鍵之類的。

clipboard.png

new 一個事件對象

CustomEvent() --mdn

document.body.onclick=function(e){console.log(e)}
var btn=document.body;
var event= new CustomEvent("click");
btn.dispatchEvent(event);

其實這裏我們可以自定義事件的名稱,然後我們就可以實現一個發佈訂閱的功能

document.addEventListener("bus", function(e) { console.log(e, e.detail) });
var event = new CustomEvent("bus", {detail: {LN_type: 'lilnong.top'}});
document.dispatchEvent(event);

event loop (事件循環)

首先,我們要牢記一件事情 js 是單線程
Event Loop 中文叫事件循環。是瀏覽器內部的一種機制,javaScript 單線程運行時如何不阻塞 UI
Javascript 有一個 main thread 主線程call-stack 調用棧(執行棧),所有的任務都會被放到調用棧(棧採用的是後進先出的規則)等待主線程執行。

任務類別&任務隊列(Task Queue)

JavaScript 中,任務被分爲兩種,一種宏任務(MacroTask)也叫Task,一種叫微任務(MicroTask)

MacroTask(宏任務)

<script>setTimeoutsetIntervalsetImmediateI/OUI Rendering
異步任務會在有了結果後,將註冊的回調函數放入任務隊列中等待主線程空閒的時候(調用棧被清空),被讀取到棧內等待主線程的執行。

MicroTask(微任務)

Process.nextTick(Node獨有)、PromiseMutationObserver
每個宏任務執行完畢後,會檢查 microTask 隊列是否有回調,會按照先入先出的規則執行,都執行完再執行宏任務,如此循環。

調用棧

棧採用的是後進先出的規則,這裏我們調用 a()a() 內部會調用 aa(), aa() 內部又調用 aa()

function a(){return aa()}
function aa(){return aaa()}
function aaa(){return 1}
  1. a 進棧
  2. aa 進棧
  3. aaa 進棧
  4. aaa 出棧
  5. ...

事件循環的進程模型

  1. 選擇任務隊列中最先進入的任務,如果任務隊列爲空,則執行跳轉到微任務(MicroTask)的執行步驟
  2. 任務設置爲已選擇任務
  3. 執行任務
  4. 任務設置爲空
  5. 運行完成的任務從任務隊列中刪除
  6. MicroTasks 步驟:

    1. 進入 MicroTask 檢查點
    2. 設置 MicroTask 檢查點標誌爲 true
    3. 當事件循環 MicroTask 不爲空時:

      1. 選擇最先進入隊列的任務,
      2. 設置爲已選擇的任務
      3. 運行
      4. 將已經執行完成的 MicroTask 改變狀態
      5. 移出 MicroTask
    4. 清理IndexDB事務
    5. 設置 MicroTask 檢查點的標誌爲false。
  7. 更新界面渲染。
  8. 返回第一步。

舉個栗子(常問無聊題)

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

new Promise(function(reslove){
    console.log('Promise-start')
    reslove();
}).then(function() {
  console.log('Promise-end');
})
console.log('script end');

結構應該沒錯

  1. 任務入棧(代碼塊)
  2. console.log('script start'); 棧中,同步代碼,直接輸出
  3. function() {console.log('setTimeout');}MacroTask
  4. new Promise 同步代碼,執行
  5. 入棧 function(reslove){console.log('Promise-start');reslove();}
  6. 執行 console.log('Promise-start');
  7. 出棧
  8. .then(function() {console.log('Promise-end');})MicroTask
  9. console.log('script end'); 同步代碼,輸出
  10. 當前執行完出棧,判斷 MicroTasks
  11. 執行 console.log('Promise-end');
  12. 完成所有 MicroTasks
  13. 渲染 UI
  14. MacroTasks是否有數據?
  15. 執行 MacroTasks 中第一個。
  16. console.log('setTimeout'); 輸出。

clipboard.png

異步事件(消息)

  1. DOM 事件
  2. setTimeout
  3. XHR
  4. Promise

總結

  1. 事件機制

    1. 當前執行塊
    2. 當前執行塊的微任務隊列
    3. 宏任務隊列
  2. Event 事件級別
  3. addEventListener 要主要保存 function 的引用,用於解綁
  4. 隊列,先進先出(想起了梗,吃多了拉)
  5. 堆棧,先進後出(想起了梗,吃多了吐)
  6. 觸發階段 捕獲>目標>冒泡
  7. Event 對象,針對不同的類型,有自己獨特的屬性。

微信公衆號:前端linong

clipboard.png

初級階段文章目錄

  1. 前端培訓-初級階段(17) - 數據存儲(cookie、session、stroage)
  2. 前端培訓-初級階段(13) - 正則表達式
  3. 前端培訓-初級階段(13) - 類、模塊、繼承
  4. 前端培訓-初級階段(13) - ECMAScript (內置對象、函數)
  5. 前端培訓-初級階段(13) - ECMAScript (語法、變量、值、類型、運算符、語句)
  6. 前端培訓-初級階段(13、18)
  7. 前端培訓-初級階段(9 -12)
  8. 前端培訓-初級階段(5 - 8)
  9. 前端培訓-初級階段(1 - 4)

資料

  1. 前端培訓目錄、前端培訓規劃、前端培訓計劃
  2. JavaScript系列----事件機制
  3. 事件參考--mdn
  4. tasks-microtasks-queues-and-schedules
  5. 一次弄懂Event Loop(徹底解決此類面試問題) --光光同學-juejin
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章