經過發佈訂閱模式實現的事件委託

關於這篇文章的背景

以前瞭解到的事件代理很少,就像是一個dom將事件委託給另外一個dom,又叫事件委託。後來作了個題目,要實現一個相似jquery的事件委託方法,而後認真的瞭解了一下。而後專一於實現,其實並無去看jquery的源碼,hhh。javascript

發佈訂閱模式大概是目前前端框架使用的一種最多見的設計模式了,而我目前也只對發佈訂閱模式有必定了解,其餘的設計模式待後續學習整理。html

關於事件委託實現

在jquery中,實現事件委託只須要下面這一行代碼就能夠搞定前端

$("#container").on('click', 'item', fn)
複製代碼

以前會以爲這麼用理所固然,因此並無想到哪一天我會親手去實現一個這樣的功能,如今機會來了。java

題目給的很簡單,就是將子節點的事件綁到其父節點上,好比下面這段dom,就是將<li>的事件綁定到<ul>上,實現點擊<li>時觸發<ul>上的事件。jquery

<div id="container">
    <li class="item" id="item1">
        <span id="btn1">hello</span>
    </li>
    <li class="item">
        <span>world</span>
    </li>
</div>
複製代碼

題目的js部分是這樣的設計模式

!function(root, doc){
    class Delegator {
        constructor (selector/* root選擇器 */) {
        // TODO
        }

        on (event/* 綁定事件 */, selector/* 觸發事件節點對應選擇器 */, fn/* 觸發函數 */) {
          // TODO
        }

        destroy () {
        // TODO
        }
    }
}(window,document)
複製代碼

而後實現一個功能相似上面jquery的事件委託,就像下面這樣的前端框架

var delegator = new Delegator('#container');
delegator.on('click', 'li.item', fnli)
複製代碼

忘記一開始想的是什麼方法了,反正寫到一半的時候忽然想起了訂閱發佈模式(由於恰好那幾天在看發佈訂閱模式),而後開始擼代碼。框架

分解上面的方法,其實能夠看做是一個監聽器dom

delegator.addEventListener('click', delegatorFn)
複製代碼

只不過這個delegatorFn有點特殊,它須要遍歷這個全部委託‘click’事件給delegator的子節點,並執行他們的委託fnli函數

首先,咱們把全部的委託看成是一個訂閱事件,只不過這個事件裏包含了委託者。在Delegator對象的原型裏增長一個屬性eventObj,裏面存放訂閱‘click’事件的全部的委託者和委託事件,結構差很少是這樣的

this.eventObj.click=[{
    selecter:'li.item',
    callback:fnli
}]
複製代碼

那麼整個on的方法其實就是委託者selector訂閱事件並委託給調on的被委託者

on (event/* 綁定事件 */, selector/* 觸發事件節點對應選擇器 */, fn/* 觸發函數 */) {
      // TODO
    
      if(!this.eventsObj[event]){
        this.eventsObj[event] = [];
      }
      this.eventsObj[event].push({
          selector,
          callback: fn
      })
      //這裏委託事件給this.root,當被委託者堅挺到event事件時,會觸發委託函數delegatorFn
      this.root.addEventListener(event, delegatorFn);
      //由於on能夠鏈式調用,因此這裏須要返回
      return this;
    }
複製代碼

如今須要實現delegatorFn方法了,其實也很簡單,主要是遍歷事件通過的全部dom中哪一個selector訂閱了它,它就執行對應節點攜帶的fn

delegatorFn = (e) => {
    let target = e.target;//觸發事件的selector
    let currentTarget = e.currentTarget; //被委託者
        //判斷觸發事件的節點及它冒泡通過的節點是不是被委託者,如果,則表示再也不有委託者,無需遍歷
    while(target !== currentTarget){
        this.eventsObj[e.type].forEach(item => {
            //查找訂閱事件者
            if(target.matches(item.selector)){
                //執行訂閱事件者攜帶的函數
                item.callback.call(target,e);
            }
        });
        //往上冒泡
        target = target.parentNode;
    }
}
複製代碼

以上,就是經過發佈訂閱實現的事件委託的核心部分,主要涉及的還有事件的冒泡。

總結

主要涉及知識點:

  • 鏈式調用:在on方法裏返回this
  • 事件冒泡:對e.target進行往上查找並執行觸發事件的回調
  • 事件訂閱
相關文章
相關標籤/搜索