標籤:sse 使用 targe https ret turn 委託 inner pre
概念
事件委託,通俗來說就是將元素的事件委託給它的父級或者更外級元素處理。
事件流
事件流描述的是從頁面中接收事件的順序。
事件冒泡:事件開始由最具體的元素接收,然後逐級向上傳播到較為不具體的節點(或文檔)。
事件捕獲:事件開始由不太具體的節點接收,然後逐級向下傳播到最具體的節點。它與事件冒泡是個相反的過程。
DOM2
級事件規定的事件流包括三個階段:
原理
事件委託就是利用事件冒泡機制實現的。
假設有一個列表,要求點擊清單項目彈出對應欄位。
<ul id="myLink"> <li id="1">aaa</li> <li id="2">bbb</li> <li id="3">ccc</li></ul>
不使用事件委託
var myLink = document.getElementById(‘myLink‘);var li = myLink.getElementsByTagName(‘li‘);for(var i = 0; i < li.length; i++) { li[i].onclick = function(e) { var e = event || window.event; var target = e.target || e.srcElement; alert(e.target.id + ‘:‘ + e.target.innerText); };}
這樣做存在的問題:
- 給每個清單項目都綁定事件,消耗記憶體
- 當有動態添加的元素時,需要重新給元素繫結事件
使用事件委託
var myLink = document.getElementById(‘myLink‘);myLink.onclick = function(e) { var e = event || window.event; var target = e.target || e.srcElement; if(e.target.nodeName.toLowerCase() == ‘li‘) { alert(e.target.id + ‘:‘ + e.target.innerText); }};
上述代碼是將事件委託給清單項目的父級,通過 target
下的 nodeName
屬性作出判斷。
也可以給每個清單項目綁定與其對應的事件。如:
var myLink = document.getElementById(‘myLink‘);myLink.onclick = function(e) { var e = event || window.event; var target = e.target || e.srcElement; switch(target.id) { case ‘1‘: target.style.backgroundColor = ‘red‘; break; case ‘2‘: alert(‘這是第二項‘); break; case ‘3‘: alert(e.target.id + ‘:‘ + e.target.innerText); break; default: alert(‘...‘); }};
上述代碼是通過判斷 target
下的 id
屬性,執行不同的事件。
事件委託的優點:
- 只需要將同類元素的事件委託給父級或者更外級的元素,不需要給所有元素都綁定事件,減少記憶體空間佔用,提升效能
- 動態新增的元素無需重新綁定事件
需要注意的地方:
- 事件委託的實現依靠事件冒泡,因此不支援事件冒泡的事件就不適合用事件委託。
最適合採用事件委託技術的事件包括 click
、mousedown
、mouseup
、keydown
、keyup
和 keypress
。雖然 mouseover
和 mouseout
事件也冒泡,但要適當處理它們並不容易,而且經常需要計算元素的位置。(因為當滑鼠從一個元素移到其子節點時,或者當滑鼠移出該元素時,都會觸發 mouseout
事件。)
- 不是所有的事件綁定都適合使用事件委託,不恰當使用反而可能會導致不需要綁定事件的元素也被綁定上了事件。
Jquery中的事件委託
jquery中實現事件委託的幾種方法:
on(events, [selector], [data], fn)
// 將 li 的事件委託給它的父元素$(‘#myLink‘).on(‘click‘, ‘li‘, function() { // todo...});
該方法在 jquery 1.7
版本已被廢棄。
delegate(selector, [type], [data], fn)
$(‘#myLink‘).delegate(‘li‘, ‘click‘, function() { // todo...});
該方法在 jquery 3.0
版本已被廢棄。用 on()
代替。
在 jquery
中, delegate()
、live()
、 one()
、bind()
等最終都是依賴 on()
方法實現的。因此可以直接使用 on()
替代其他方法。
封裝一個事件委託方法
需要注意的地方:
- 保證相容性,包括:事件綁定、元素選取器
Element.matches
、事件 event
對象
- 回呼函數
this
指向
- 上面的樣本中,當目標元素下還有子項目時,子項目不能觸發事件。解決辦法是在觸發過程中對元素進行判斷,如果當前觸發的元素不是目標元素,就繼續往該元素的
parentNode
尋找,否則迴圈結束。
/** * [delegateEvent description] * @param {[type]} parentSelector 父元素 * @param {[type]} targetSelector 目標元素 * @param {[type]} events 事件 * @param {Function} fn 回呼函數 * @return {[type]} null */function delegateEvent(parentSelector, targetSelector, events, fn) { // 事件綁定相容性處理 function addEvent(ele, type, handle) { if(ele.addEventListener) { ele.addEventListener(type, handle, false); } else if(ele.attachEvent){ ele.attachEvent(‘on‘ + type, handle); } else { ele[‘on‘ + type] = handle; } } // 如果元素被指定的選取器字串選擇,Element.matches() 方法返回 true; 否則返回 false。 // 對於不支援 Element.matches() 或 Element.matchesSelector(),但支援 document.querySelectorAll() 方法的瀏覽器,存在以下替代方案 if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) !== this) {} return i > -1; }; } // 事件處理邏輯 addEvent(parentSelector, events, function(e) { // 相容性處理 var e = e || window.event; var t = e.target || e.srcElement; // currentTarget === parentSelector var currentTarget = e.currentTarget; // 遍曆並判斷是否為目標元素,如果不是,則往元素的 parentNode 繼續尋找 while(!t.matches(targetSelector)) { // 如果是目標元素則跳出迴圈 if(t === currentTarget) { t = null; break; } t = t.parentNode; } if(t) { // 將回呼函數的 this 指向目標元素 fn.call(t, Array.prototype.slice.call(arguments)); } });}
調用樣本:
<ul id="myLink"> <li id="1" class="link"><a href="javascript:;"><span>aaa</span></a></li> <li id="2" class="link"><a href="javascript:;">bbb</a></li> <li id="3" class="link">ccc</li></ul>
var myLink = document.querySelector(‘#myLink‘);delegateEvent(myLink, ‘li.link‘, ‘click‘, function() { console.log(this, this.id + ‘:‘ + this.innerText);});
參考資料:
1、《JavaScript進階程式設計(第3版)》
*2、Element.matches() API https://developer.mozilla.org/zh-CN/docs/Web/API/Element/matches*
JavaScript事件委託原理及Jquery中的事件委託