整理DOM事件相關知識點

DOM事件相關內容

當用戶與瀏覽器發生的一些交互時, 若是但願去得到用戶行爲, 就須要藉助事件來完成. 事件部份內容在 JS中重要性不言而喻. html

羅列須要瞭解與事件相關的知識以下: 這也是面試中遇到的問題.面試

  • DOM 事件的級別
  • DOM 事件模型
  • DOM 事件流
  • DOM 事件處理程序
  • 描述DOM事件捕獲(冒泡)的具體流程
  • Event對象常見的應用
  • 自定義事件

事件級別

  • DOM0
  • DOM2
  • DOM3

DOM 事件模型

  • 事件冒泡
  • 事件捕獲

什麼是事件流

要想明白事件流,必須先懂的這幾個知識點後端

  • 事件冒泡
  • 事件捕獲

先來看一個有趣的問題, 這是 4 代瀏覽器(IE4)開發團隊遇到一個的問題:瀏覽器

What part of the Webpage owns a specific event?

頁面的哪一部分會擁有某個特定的事件?框架

要想明白這個問題能夠想象成在一個頁面上畫了一組同心圓, 當手指放在中間時,它不只在一個圓圈內,並且在全部的圓圈內。

以下圖所示:函數

圖片描述

這就是瀏覽器事件的工做原理, 當你點擊一個按鈕時, 不只單擊按鈕,還單擊包含的容器和整個頁面。工具

事件生命週期, 分爲三個階段: capturing (捕獲), target (目標), bubbling (冒泡). 而這三個階段也是構成事件流基本部分. 開發工具

這種處理思想, 在如今流行 NodeJS 後端框架 Koa 對中間件的處理模式也是基於這種, 熟悉Koa或許對這洋蔥圖並不會陌生:this

圖片描述

先來熟悉事件的模型:spa

Javascript Events: Event bubbing (事件冒泡)

事件冒泡: 既事件開始由最具體的元素接收,而後逐級向上傳播最後到達 Document 對象 或 window 上.

圖片描述

先來看一個簡單的示例, 代碼以下:

<!DOCTYPE HTML>
    <html>
        <head>
            <title>......</title>
        </head>
        <body>
            <div id="demo"> Press here.</div>
        </body>
    </html>
var target = document.getElementById("demo");
    window.addEventListener("click", function(){
        console.log("window bubbling");
    });
    document.addEventListener("click", function(){
        console.log("document bubbling");
    });
    document.documentElement.addEventListener("click", function(){
        console.log("html bubbling");
    });
    document.body.addEventListener("click", function(){
        console.log("body bubbling");
    });
    target.addEventListener("click", function(){
        console.log("target bubbling");
    });

控制檯打印輸出以下

"target bubbling"
    "body bubbling"
    "html bubbling"
    "document bubbling"
    "window bubbling"

當一個div 被點擊, 這點擊事件發生的順序以下:

  • div
  • body
  • html
  • Document
  • Window (如今瀏覽器中, IE9+, Firfox, Chrome 等)

從執行順序來講, click事件首先在 div 元素上觸發, 而後沿着DOM Tree 向上傳播, 在路徑上的每一個節點上觸發,直到它到達文檔對象(或Window對象)。

Javascript Events: Event Capturing (捕獲)

圖片描述

事件捕獲 是另一種事件流模型, 最早由 Netscape Browser 引入.

根據上面的的模型, 恰好與前面冒泡相反. 根據該模型,最不特定(最外層)的節點首先接收事件,而最特定(目標元素)的節點最後接收事件.

它設計的目標就是在事件到達目標以前,事先進行攔截.

參考前面的示例, 修改下監聽方式, 代碼修改以下:

var target = document.getElementById("demo");
    window.addEventListener("click", function(){
        console.log("window Capturing");
    }, true);
    document.addEventListener("click", function(){
        console.log("document Capturing");
    }, true);
    document.documentElement.addEventListener("click", function(){
        console.log("html Capturing");
    }, true);
    document.body.addEventListener("click", function(){
        console.log("body Capturing");
    }, true);
    target.addEventListener("click", function(){
        console.log("target Capturing");
    }, true);

打印的結果:

"window bubbling"
    "document bubbling"
    "html bubbling"
    "body bubbling"
    "target bubbling"

當點擊 div 元素, 按照以下順序來進行廣播事件.

  • Window (如今瀏覽器中, IE9+, Firfox, Chrome 等)
  • Document
  • Document
  • body
  • div

注意:

DOM0 級中默認就是使用冒泡的方式, 不支持捕獲的. 因此在 DOM2 級中經過 addEventListener 提供的第三個參數來控制使用哪一種事件流來處理.

Javascript Events: DOM Event Flow (事件流)

根據上面兩種模型, 能夠總結完整事件流應該向以下:

圖片描述

DOM Level 2 Events 指定的事件流模型分爲三個階段:

  • Event Capturing Phase (事件捕獲階段)
  • At the target (目標階段)
  • Event Bubbling Phase (事件冒泡階段)

從上面流程圖中, 首先發生的是 事件捕獲階段 爲截獲事件提供了機會, 再到目標階段 而後進入 事件冒泡階段, 能夠在這個階段對事件進行響應.

引用前面的例子, 點擊 DIV 元素時, 事件將按照上圖順序進行觸發.

這就是完整的事件流, 內容看起來挺多的, 實際上一句話就概述事件流.

用來描述事件發生順序(頁面接收事件順序).

事件處理方式

經過前面的知識點, 事件就是表示用戶或瀏覽器自身執行某種動做. 例如: click, dbclick, load, unload, mouseover,mouseout, mouseenter ,mouseleave 等等, 這些都是事件的名字. 而響應並處理某個事件的函數稱爲 事件處理程序(或事件監聽器(觀察者模式)).

常見事件處理方式包括以下幾種:

  • HTML 事件處理程序
  • DOM0 級處理程序
  • DOM2 級處理程序

HTML 事件處理程序

直接來看個簡單示例:

<input type="button" onclick="alert('<Clicked')"/>

這種模式, 能夠理解爲 CSS 行內樣式

<div style="color: #ccc;"></div>

直接在 HTML 元素上綁定相應事件名, 並指定對應的事件處理程序. 從前面語法來講, 事件處理程序是一個函數, 上面傳遞是一個語句, 也就是說默認執行時, JS引擎會進行相應處理. 等價於這種方式:

<input type="button" onclick="(function(){alert('Clicked')})()"/>

若是把事件處理函數直接以這種內聯值的方式提供, 應該注意不能值中指定未經轉義的HTML語法字符 例如: 和號(&) 、雙引號("") 等等, 不然會解析出錯.

如上面示例,若是想使用雙引號, 必須這麼處理.

<input type="button" onclick="alert(&quot;Clicked&quot;)"/>

這種對於簡單語句還行, 除了提供元素屬性值的方式, 那麼有木有其它方式咧? 答案:固然有 , HTML事件處理程序能夠調用在頁面其它地方定義的腳本.

示例以下:

<input type="button" onclick="handleClick()"/>
    <script>
        function handleClick(){
            alert("Clicked");
        }
    </script>

經過這種方式指定事件處理程序具備一些特別的地方.

  • 前面講述事件處理程序引擎在執行時, 默認封裝函數. 在封裝的函數中包含一個局部變量 event, 也就是事件對象.
<input type="button" onclick="alert(event)"/>

    // => 等價

    <input type="button" onclick="(function(){var e = event; alert(e);})()"/>
  • 該函內部 this 指針表示是當前的目標對象
<input type="button" onclick="alert(this)"/>

    // this === [object HTMLInputElement] === input元素

這樣能夠經過 this來獲取目標元素相關的內容了. 好比取值

<input type="button" onclick="alert(this.value)"/>
    <!--或簡寫成這樣-->
    <input type="button" value="Click Me" onclick="alert(value)"/>

爲何能夠簡寫方式, input 對象是存在於當前函數的做用域鏈中(scope). 爲了調試方式, 把上面方式做以下改變

<input type="button" value="Click Me" onclick="(function aa(){debugger;console.log(value)})()"/>

其實理解上面那句話, 咱們能夠藉助開發這工具來協助理解:

圖片描述

  • 從執行棧(Call Stack)能夠看出, 證實前面說的默認會建立封裝函數 .
  • 從 Scope 做用鏈中能夠看出, 前面說的 input 對象會被添加在當前匿名函數執行的做用域鏈上.

根據開發工具來看, 咱們是能夠模擬引擎幫作的事情, 僞代碼以下:

function () {
        with(document) {
            with(input){
                // dosomething
                console.log(value);    
            }
        }
    }

本質經過 with 來擴展做用域.

上面把基本內容說, 那麼這種經過HTML事件處理程序有什麼問題麼 ?

  • 時差問題

    事件處理程序必須優先元素加載, 有可能DOM加載出來, 用戶就開始操做, 此時事件處理程序可能未加載致使報錯

  • 前面說起擴展程序的做用域鏈可能在不一樣瀏覽器中兼容不同,致使程序出現錯誤
  • 視圖和行爲偶合在一塊兒,也就是說事件處理程序修改相應JS部分、HTML部分都須要去修改.

引用前面的示例:

<input type="button" onclick="handleClick()"/>
    <script>
        function handleClick(){
            alert("Clicked");
        }
    </script>

若是用戶想把函數名爲: "onClick", 這時是否是須要同時去修改.

未完, 後續...

相關文章
相關標籤/搜索