国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

phodal / 2129人閱讀

前言

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

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

先提幾個(gè)引子:

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

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

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

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

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

如果這幾個(gè)內(nèi)容你都熟悉了,那么這篇文章不會(huì)給你帶來太多的幫助.

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

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

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

在下方的表格中我們記錄了不同瀏覽器之間click點(diǎn)擊后event可用的屬性列表(刪除了控制臺(tái)輸出的原型和函數(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

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

當(dāng)然這篇文章可不是將所有的Event屬性都將一遍,要知道不同的事件的Event對(duì)象結(jié)構(gòu)是不同的.

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

currentTarget,target,srcElement,this currentTarget

一句話:

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

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

this

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

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

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

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

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

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

    // 注意我們監(jiān)聽的是wrap的click事件,而不是button的click事件
    wrap.addEventListener("click",function (event) {
      
      // event.target指向的是按鈕,因?yàn)槲覀凕c(diǎn)擊的是按鈕
      console.log(event.target === button && event.target === event.srcElement); // true

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

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

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

this 和 currentTarget指向的都是添加了監(jiān)聽器的對(duì)象這里就是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 引用著其父級(jí)的
      元素. e.target.style.visibility = "hidden"; } // 添加監(jiān)聽事件到列表,當(dāng)每個(gè)
    • 被點(diǎn)擊的時(shí)候都會(huì)觸發(fā)。 ul.addEventListener("click", hide, false);
  • https://developer.mozilla.org...
    srcElement

    簡(jiǎn)單理解event.srcElement === event.target.

    Event.srcElement 是標(biāo)準(zhǔn)的 Event.target 屬性的一個(gè)別名。它只對(duì)老版本的IE瀏覽器有效。

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

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

    完整的事件編程 EventTarget接口

    當(dāng)我們?cè)谑褂萌缦碌姆椒ǖ臅r(shí)候:

    elem.addEventListener

    elem.removeEventListener

    elem.dispatchEvent

    實(shí)際上是在使用EventTarget接口上的功能.

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

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

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

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

    繼承EventTarget而不是直接使用EventTarget的實(shí)例,

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

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

    首先我們來繼承EventTarget對(duì)象:

    繼承EventTarget

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

    你可以在控制臺(tái)選擇一個(gè)HTML元素一路查找原型鏈得到.

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

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

        // --- 包裝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結(jié)束
    
        // --- 創(chuàng)建我們繼承EventTarget的構(gòu)造函數(shù)
        function myElem() {
          
        }
    
        myElem.prototype = new MyEventTarget;
        myElem.prototype.constructor = myElem;
    
        // 創(chuàng)建實(shí)例
        const instance = new myElem();
    
    instance.addEventListener("click",()=>{
        // 現(xiàn)在我們實(shí)例可以監(jiān)聽事件了
    });
    
        console.log(instance);

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

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

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

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

    詳細(xì)參考:

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

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

    獲取一個(gè)event:

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

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

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

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

    現(xiàn)在我們來創(chuàng)建一個(gè)標(biāo)準(zhǔn)的Event對(duì)象:

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

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

    event.target

    event.currentTarget

    event.preventDefault()

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

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

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

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

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

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

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

    其他的構(gòu)造函數(shù)請(qǐng)參考這里.

    觸發(fā)我們的事件

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

    在我們之前創(chuàng)建的實(shí)例上進(jìn)行事件的觸發(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)聽事件并且打印實(shí)例
        });
    
        const myEvent = new Event("test"); // 創(chuàng)建Event實(shí)例
    
        instance.dispatchEvent(myEvent); // 觸發(fā)事件
    

    當(dāng)你調(diào)用dispatchEvent的時(shí)候,EventTarget會(huì)按照對(duì)應(yīng)事件注冊(cè)的順序來同步執(zhí)行這些事件監(jiān)聽器.

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

    詳細(xì)參考

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

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

    我們?cè)陧撁嬷衼硪淮尉唧w的實(shí)戰(zhàn),首先建立如下的HTML結(jié)構(gòu):

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

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

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

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

    結(jié)論很明確:

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

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

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

    轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/109251.html

    相關(guān)文章

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

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

      lk20150415 評(píng)論0 收藏0
    • 淺析JavaScript異步

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

      Tangpj 評(píng)論0 收藏0
    • DOM事件模型淺析

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

      banana_pi 評(píng)論0 收藏0
    • DOM事件模型淺析

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

      rubyshen 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
    <