成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

重探瀏覽器事件(淺析事件編程化)

phodal / 2137人閱讀

前言

在平常開發(fā)過程中,就算不使用現(xiàn)在主流的框架也至少得使用個Jquery,這些工具幫我們統(tǒng)一不同瀏覽器平臺之間的差異和細節(jié),可以將注意力集中到開發(fā)上來.

不過有意思的一點是,在看完高程的N年后我居然連event對象中的target和currentTarget屬性的區(qū)別都忘記了.

先提幾個引子:

你能說出event.currentTarget和event.target的區(qū)別嗎?

如果可以那么event.srcElement和事件監(jiān)聽函數(shù)中的this呢

如何使用編程的方式來觸發(fā)事件,而不借助瀏覽器默認觸發(fā)方式?

如何創(chuàng)建一個我們自己的Event對象,然后自定義我們的事件?

實現(xiàn)上方的內容的同時該如何兼容IE瀏覽器?

如果這幾個內容你都熟悉了,那么這篇文章不會給你帶來太多的幫助.

在正文開始之前先來瀏覽一個表格,來看一下不同瀏覽器之間Event對象的屬性有何不同:

  var button = document.getElementById("button");

  button.addEventListener("click",function (event) {
    console.log(event);
  });

在下方的表格中我們記錄了不同瀏覽器之間click點擊后event可用的屬性列表(刪除了控制臺輸出的原型和函數(shù)引用):

firefox67 chrome72 edge44.17763.1.0 ie11 ie9
altKey altKey altKey altKey altKey
bubbles bubbles bubbles AT_TARGET AT_TARGET
button button button bubbles bubbles
buttons buttons buttons BUBBLING_PHASE BUBBLING_PHASE
cancelBubble cancelBubble cancelable button button
cancelable cancelable cancelBubble buttons buttons
clientX clientX clientX cancelable cancelable
clientY clientY clientY cancelBubble cancelBubble
composed composed ctrlKey CAPTURING_PHASE CAPTURING_PHASE
ctrlKey ctrlKey currentTarget clientX clientX
currentTarget currentTarget defaultPrevented clientY clientY
defaultPrevented defaultPrevented detail constructor constructor
detail detail eventPhase ctrlKey ctrlKey
eventPhase eventPhase fromElement currentTarget currentTarget
explicitOriginalTarget fromElement height defaultPrevented defaultPrevented
isTrusted isTrusted isPrimary detail detail
layerX layerX isTrusted deviceSessionId eventPhase
layerY layerY layerX eventPhase fromElement
metaKey metaKey layerY fromElement isTrusted
movementX movementX metaKey height layerX
movementY movementY movementX hwTimestamp layerY
mozInputSource offsetX movementY isPrimary metaKey
mozPressure offsetY offsetX isTrusted offsetX
offsetX pageX offsetY layerX offsetY
offsetY pageY pageX layerY pageX
originalTarget path pageY metaKey pageY
pageX relatedTarget pointerId offsetX relatedTarget
pageY returnValue pointerType offsetY screenX
rangeOffset screenX pressure pageX screenY
rangeParent screenY relatedTarget pageY shiftKey
region shiftKey returnValue pointerId srcElement
relatedTarget sourceCapabilities screenX pointerType target
returnValue srcElement screenY pressure timeStamp
screenX target shiftKey relatedTarget toElement
screenY timeStamp srcElement rotation type
shiftKey toElement target screenX view
srcElement type tiltX screenY which
target view tiltY shiftKey x
timeStamp which timeStamp srcElement y
type x toElement target
view y twist tiltX
which type tiltY
x view timeStamp
y which toElement
width type
x view
y which
width
x
y

通過這個表格我們可以觀察Event對象在不同瀏覽器之間結構是不同的,出人意料的是即使是在現(xiàn)代瀏覽器中事件對象也存在著差異.

當然這篇文章可不是將所有的Event屬性都將一遍,要知道不同的事件的Event對象結構是不同的.

吐槽:本來是打算提供IE8的但是ie8不能使用addEventListener來監(jiān)聽事件懶得去搞ie那套數(shù)據(jù)了.

currentTarget,target,srcElement,this currentTarget

一句話:

哪個元素上監(jiān)聽的事件,event.currentTarget返回的就是這個對象的本身的引用.

如果你的一個事件監(jiān)聽函數(shù)被注冊到了多個DOM元素上,利用這個屬性你就可以判斷是誰觸發(fā)的事件.

this

回調函數(shù)中的this === event.currentTarget.

button.addEventListener("click",function (event) {
      console.log(event.currentTarget === this); // true
});
target

event.target和上面的三者不同,這里面涉及到了DOM中的一個基本知識事件冒泡和事件攔截.

關于這兩點我相信大家都已經(jīng)了解了,即使不了解網(wǎng)上介紹的文章也有一大堆.

我們用事件冒泡來舉例,并且改寫我們之前的那個例子:

  
    var 
      wrap = document.getElementById("wrap"),
      button = document.getElementById("button");

    // 注意我們監(jiān)聽的是wrap的click事件,而不是button的click事件
    wrap.addEventListener("click",function (event) {
      
      // event.target指向的是按鈕,因為我們點擊的是按鈕
      console.log(event.target === button && event.target === event.srcElement); // true

      // 當我們點擊按鈕觸發(fā)的事件冒泡到了wrap,所以觸發(fā)了wrap的click事件,
      // 此時currentTarget指向的是wrap
      console.log(wrap===this && wrap === event.currentTarget); // true

      // 直接打印event然后控制臺中查看currentTaget會返回null
      // 你可以將他賦值到一個變量在打印輸出這個變量
      // see https://github.com/vuejs/vue/issues/6867#issuecomment-338195468
    })

在這個例子中,我們點擊頁面中的按鈕,然后再按鈕的包裹div中接收到了button冒泡上來的事件,這其中:

this 和 currentTarget指向的都是添加了監(jiān)聽器的對象這里就是wrap

target 和 srcElement指向的是觸發(fā)了事件的元素

事件委托也是event.target最常見的用途之一:

// Make a list
var ul = document.createElement("ul");
document.body.appendChild(ul);

var li1 = document.createElement("li");
var li2 = document.createElement("li");
ul.appendChild(li1);
ul.appendChild(li2);

function hide(e){
  // e.target 引用著 
  • 元素 // 不像 e.currentTarget 引用著其父級的
      元素. e.target.style.visibility = "hidden"; } // 添加監(jiān)聽事件到列表,當每個
    • 被點擊的時候都會觸發(fā)。 ul.addEventListener("click", hide, false);
  • https://developer.mozilla.org...
    srcElement

    簡單理解event.srcElement === event.target.

    Event.srcElement 是標準的 Event.target 屬性的一個別名。它只對老版本的IE瀏覽器有效。

    https://developer.mozilla.org...

    參考之前的表格后看來這個屬性還沒有被干掉,在目前最新的瀏覽器上它依然存在,不過已經(jīng)不建議使用,除非你需要向下兼容.

    完整的事件編程 EventTarget接口

    當我們在使用如下的方法的時候:

    elem.addEventListener

    elem.removeEventListener

    elem.dispatchEvent

    實際上是在使用EventTarget接口上的功能.

    例如我們可以創(chuàng)建一個新的EventTarget對象來添加事件監(jiān)聽:

        const a = new EventTarget;
    
        a.addEventListener("click",()=>{
          
        })

    但是這沒有任何意義,因為這里沒有事件被觸發(fā).我們僅僅是添加了事件監(jiān)聽器而已.

    為了達到我們目的通過編程的方式來執(zhí)行完整的事件流程我們還需要完成如下的幾步:

    繼承EventTarget而不是直接使用EventTarget的實例,

    在事件監(jiān)聽函數(shù)中傳遞Event對象

    找個地方來觸發(fā)這個事件

    首先我們來繼承EventTarget對象:

    繼承EventTarget

    在瀏覽器中大部分可以添加刪除事件的對象都繼承了EventTarget對象.

    你可以在控制臺選擇一個HTML元素一路查找原型鏈得到.

    但是他們進過重重繼承,都有自己的獨特屬性和事件類型甚至是不同的構造函數(shù).

    為了和已有的事件進行區(qū)分我們這里需要對EventTarget進行繼承:

        // --- 包裝EventTarget開始
        function MyEventTarget() {
          var target = document.createTextNode(null);
          this.addEventListener = target.addEventListener.bind(target);
          this.removeEventListener = target.removeEventListener.bind(target);
          this.dispatchEvent = target.dispatchEvent.bind(target);
        }
        MyEventTarget.prototype = EventTarget.prototype;
        // --- 包裝EventTarget結束
    
        // --- 創(chuàng)建我們繼承EventTarget的構造函數(shù)
        function myElem() {
          
        }
    
        myElem.prototype = new MyEventTarget;
        myElem.prototype.constructor = myElem;
    
        // 創(chuàng)建實例
        const instance = new myElem();
    
    instance.addEventListener("click",()=>{
        // 現(xiàn)在我們實例可以監(jiān)聽事件了
    });
    
        console.log(instance);

    繼承的過程看似非常復雜,尤其是包裝EventTarget顯得多此一舉.但是搞定EventTarget的繼承確實花了我大量的時間去尋找解決方案.

    你完全可以編寫自己的繼承方式來去繼承EventTarget,不過你會發(fā)現(xiàn)這其中的坑非常深.

    簡單來說,EventTarget在JavaScript中真的就是一個接口,雖然是以函數(shù)的形式存在,但是它不是構造函數(shù)(這點在Chrome64 和firefox59后進行了修改).

    總之通過原型鏈繼承的EventTarget統(tǒng)統(tǒng)無法工作,如果使用ES6的類式繼承在現(xiàn)代瀏覽器中(Chrome64和firefox59后)可以使用class來進行繼承.

    詳細參考:

    https://stackoverflow.com/que...

    創(chuàng)建我們的Event對象

    獲取一個event:

    document.getElementById("button").addEventListener("click",(event)=>{
          // event
          console.log(event);
        })
    

    如果你在瀏覽器中運行這段代碼并且在控制臺中查看,你會發(fā)現(xiàn)變量event的名稱MouseEvent,如果你沿著原型鏈向上你會發(fā)現(xiàn)繼承的是UIEvent再次向上查看則是真正的Event.

    事件觸發(fā)中傳遞的第一個參數(shù)我們通常叫它event,所有的event對象都基于Event,但是這不意味著這種關系的窗戶紙就只有一層,click事件中的event和Event之間就隔著一個UIEvent.

    通常隨著event繼承的層數(shù)越多,event對象身上的屬性也會越來越多.

    現(xiàn)在我們來創(chuàng)建一個標準的Event對象:

    // 使用全局的Event
    new Event("test",{ // 事件類型
          bubbles:false, // 是否冒泡 默認false
          cancelable:false,// 是否可以被取消 默認false
        });
    
    https://developer.mozilla.org...

    如果你在瀏覽器中觀察這個對象,你會發(fā)現(xiàn)事件上常見的屬性諸如:

    event.target

    event.currentTarget

    event.preventDefault()

    都在這個new Event()返回的對象中,由于其他類型的事件都繼承自Event這也解釋了為什么事件對象中總是有這些屬性.

    和繼承EventTarget一樣,使用Event的過程也同樣艱難,總的來說使用Event的難點在于它有兩套API:

    第一套比較新的API提供了現(xiàn)代的接口,也就是之前例子中的方式.
    在創(chuàng)建一個已有的事件的時候,你只需要使用全局的構造函數(shù)就可以,
    例如:new MouseEvent("test",/*對應MouseEvent的參數(shù)選項*/),
    但是缺點就是不支持IE瀏覽器.

    第二套API支持IE瀏覽器,但是使用過程比較繁瑣
    使用Event.createEvent(/*事件類型*/)創(chuàng)建對應事件類型的Event對象,
    使用Event.initEvent()來初始化事件,并且提供對應事件類型的參數(shù),
    如果你創(chuàng)建一個MouseEvent類型的事件InitEvent方法最多需要15個參數(shù).
    這種情況下使用new MouseEvent()傳入對象配置的形式就簡單多了.

    一篇值得參考的文章,使用createEvent api

    https://www.cnblogs.com/ggz19...

    此外不同種類的事件,都有自己的全局構造函數(shù),不同類型的構造函數(shù)的第二個參數(shù)中的選項也是不同的.

    其他的構造函數(shù)請參考這里.

    觸發(fā)我們的事件

    觸發(fā)事件就顯得簡單多了,我們需要使用EventTarget.dispatchEvent方法.

    在我們之前創(chuàng)建的實例上進行事件的觸發(fā):

        function MyEventTarget() {
          var target = document.createTextNode(null);
          this.addEventListener = target.addEventListener.bind(target);
          this.removeEventListener = target.removeEventListener.bind(target);
          this.dispatchEvent = target.dispatchEvent.bind(target);
        }
        MyEventTarget.prototype = EventTarget.prototype;
        function myElem() {
    
        }
    
        myElem.prototype = new MyEventTarget;
        myElem.prototype.constructor = myElem;
    
        const instance = new myElem();
    
        instance.addEventListener("test", (event) => {
          console.log(event); // 監(jiān)聽事件并且打印實例
        });
    
        const myEvent = new Event("test"); // 創(chuàng)建Event實例
    
        instance.dispatchEvent(myEvent); // 觸發(fā)事件
    

    當你調用dispatchEvent的時候,EventTarget會按照對應事件注冊的順序來同步執(zhí)行這些事件監(jiān)聽器.

    如果在事件監(jiān)聽器中調用了event.preventDefault,那么dispatchEvent就返回false反之返回true(前提是cancleable為true).

    詳細參考

    https://developer.mozilla.org...

    編程式的事件觸發(fā)

    我們在頁面中來一次具體的實戰(zhàn),首先建立如下的HTML結構:

    我們在#wrap中監(jiān)聽click事件,然后在#button觸發(fā)click事件.

    這樣我們可以練習一下Event中bubbles(允許冒泡)參數(shù)的使用,

    另外還可以測試click事件中的Event對象如果不是MouseEvent的實例那么監(jiān)聽器是否會被觸發(fā).

        const
          button = document.getElementById("button"),
          wrap = document.getElementById("wrap");
    
        wrap.addEventListener("click", (event) => {
          console.log(event); // 打印event對象
        });
    
        const myEvent1 = new Event("click", {
          bubbles: false, // 不可以冒泡
        });
    
        const myEvent2 = new Event("click", {
          bubbles: true, // 可以冒泡
        });
    
        button.dispatchEvent(myEvent1); // 這次沒有打印出內容
        button.dispatchEvent(myEvent2); // 這次打印出了內容
    

    結論很明確:

    dispatchEvent執(zhí)行的時候只要是Event的實例且類型相同那么監(jiān)聽器就會被觸發(fā).

    bubbles參數(shù)可以控制該事件是否允許冒泡

    文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉載請注明本文地址:http://systransis.cn/yun/109251.html

    相關文章

    • 重探覽器事件(淺析事件編程)

      前言 在平常開發(fā)過程中,就算不使用現(xiàn)在主流的框架也至少得使用個Jquery,這些工具幫我們統(tǒng)一不同瀏覽器平臺之間的差異和細節(jié),可以將注意力集中到開發(fā)上來. 不過有意思的一點是,在看完高程的N年后我居然連event對象中的target和currentTarget屬性的區(qū)別都忘記了. 先提幾個引子: 你能說出event.currentTarget和event.target的區(qū)別嗎? 如果可以那么ev...

      lk20150415 評論0 收藏0
    • 淺析JavaScript異步

      摘要:回調函數(shù),一般在同步情境下是最后執(zhí)行的,而在異步情境下有可能不執(zhí)行,因為事件沒有被觸發(fā)或者條件不滿足。同步方式請求異步同步請求當請求開始發(fā)送時,瀏覽器事件線程通知主線程,讓線程發(fā)送數(shù)據(jù)請求,主線程收到 一直以來都知道JavaScript是一門單線程語言,在筆試過程中不斷的遇到一些輸出結果的問題,考量的是對異步編程掌握情況。一般被問到異步的時候腦子里第一反應就是Ajax,setTimse...

      Tangpj 評論0 收藏0
    • DOM事件模型淺析

      摘要:事件發(fā)生后,對象可能會作出響應,也有可能無動于衷。事件模型在講解事件模型前,再用一個例子作為引入。當一個事件發(fā)生時,事件會在樹中進行傳播。冒泡階段在此階段,事件從事件源開始向上傳播,直到根結點。 1.何為DOM DOM是Document Object Model的縮寫,中文譯為文檔對象模型。它是一種跨平臺、跨語言的編程接口,將HTML,XHTML,XML文檔映射成樹形結構,樹的每一個節(jié)...

      banana_pi 評論0 收藏0
    • DOM事件模型淺析

      摘要:事件發(fā)生后,對象可能會作出響應,也有可能無動于衷。事件模型在講解事件模型前,再用一個例子作為引入。當一個事件發(fā)生時,事件會在樹中進行傳播。冒泡階段在此階段,事件從事件源開始向上傳播,直到根結點。 1.何為DOM DOM是Document Object Model的縮寫,中文譯為文檔對象模型。它是一種跨平臺、跨語言的編程接口,將HTML,XHTML,XML文檔映射成樹形結構,樹的每一個節(jié)...

      rubyshen 評論0 收藏0

    發(fā)表評論

    0條評論

    最新活動
    閱讀需要支付1元查看
    <