關於這篇文章的背景
之前瞭解到的事件代理不多,就像是一個dom將事件委託給另一個dom,又叫事件委託。後來做了個題目,要實現一個類似jquery的事件委託方法,然後認真的瞭解了一下。然後專注於實現,其實並沒有去看jquery的原始碼,hhh。
釋出訂閱模式大概是目前前端框架使用的一種最常見的設計模式了,而我目前也只對釋出訂閱模式有一定了解,其他的設計模式待後續學習整理。
關於事件委託實現
在jquery中,實現事件委託只需要下面這一行程式碼就可以搞定
$("#container").on('click', 'item', fn)
複製程式碼
之前會覺得這麼用理所當然,所以並沒有想到哪一天我會親手去實現一個這樣的功能,現在機會來了。
題目給的很簡單,就是將子節點的事件綁到其父節點上,比如下面這段dom,就是將<li>
的事件繫結到<ul>
上,實現點選<li>
時觸發<ul>
上的事件。
<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)
複製程式碼
忘記一開始想的是什麼方法了,反正寫到一半的時候突然想起了訂閱釋出模式(因為剛好那幾天在看釋出訂閱模式),然後開始擼程式碼。
分解上面的方法,其實可以看作是一個監聽器
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
進行往上查詢並執行觸發事件的回撥 - 事件訂閱