JavaScript事件委託原理及Jquery中的事件委託

來源:互聯網
上載者:User

標籤: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 屬性,執行不同的事件。

事件委託的優點:

  • 只需要將同類元素的事件委託給父級或者更外級的元素,不需要給所有元素都綁定事件,減少記憶體空間佔用,提升效能
  • 動態新增的元素無需重新綁定事件

需要注意的地方:

  • 事件委託的實現依靠事件冒泡,因此不支援事件冒泡的事件就不適合用事件委託。

最適合採用事件委託技術的事件包括 clickmousedownmouseupkeydownkeyupkeypress。雖然 mouseovermouseout 事件也冒泡,但要適當處理它們並不容易,而且經常需要計算元素的位置。(因為當滑鼠從一個元素移到其子節點時,或者當滑鼠移出該元素時,都會觸發 mouseout 事件。)

  • 不是所有的事件綁定都適合使用事件委託,不恰當使用反而可能會導致不需要綁定事件的元素也被綁定上了事件。
Jquery中的事件委託

jquery中實現事件委託的幾種方法:

  • on

on(events, [selector], [data], fn)

// 將 li 的事件委託給它的父元素$(‘#myLink‘).on(‘click‘, ‘li‘, function() {    // todo...});
  • live

該方法在 jquery 1.7 版本已被廢棄。

  • delegate

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中的事件委託

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.