JavaScript事件代理入門

來源:互聯網
上載者:User

事件代理(Event Delegation),又稱之為事件委託。是 JavaScript 中常用綁定事件的常用技巧。 顧名思義,“事件代理”即是把原本需要綁定的事件委託給父元素,讓父元素擔當事件監聽的職務。   為什麼要這樣做呢? 眾所周知,DOM操作是十分消耗效能的。所以重複的事件綁定簡直是效能殺手。而事件代理的核心思想,就是通過盡量少的綁定,去監聽盡量多的事件。   下面將會用 Zepto 為大家示範怎麼實現事件代理。 啊?Zepto是什嗎? Zepto is a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto. 由於API是相容 jQuery 的,熟悉jQuery的童鞋使用 Zepto 幾乎無需學習成本。示範代碼實際上也能在 jQuery 上正常運行。而目前 Zepto 的適用情境更多是在移動端,一個遠離舊版IE的世界。   那為什麼直接用jQuery? 因為下文會簡要分析源碼,但jQuery裡面為了相容舊版IE做了很多妥協,源碼十分不直觀。而 Zepto 是面向現代瀏覽器設計的,所用到的API絕大多數都符合W3C標準,分析起來更加直觀。   以這個HTML結構為例: <ul class="list">    <li class="list_items">000</li>    <li class="list_items">111 <a href="javascript:void(0);">link</a></li>    <li class="list_items">222 <i>italic</i></li>    <li>333</li></ul>  一般情況下,會這樣綁定事件: $('.list_items').on('click', function (e) {    console.log(e.target.tagName);    console.log(this.tagName);});Deom>>   開啟瀏覽器的 Console 看一下,會發現每一個 .list_items 元素都被綁定了click事件,並且綁定的對象是 li.list_items 。如:   這意味著,Zepto的實際上是遍曆了所有 .list_items 元素,並逐個綁定 click 事件。 實現思路和下面這段原生 JavaScript 代碼相同: [].forEach.call(document.querySelectorAll('.list_items'), function (elem) {  elem.addEventListener('click', function (e) {    console.log(e.target.tagName);    console.log(this.tagName);  }, false);});這樣的做法,當遇到數量超長的列表(ul)和表格(table)時效能開銷非常大。 例如:ul 中有1000個 li 時,就需要進行1000次的事件綁定。 而事件代理,就是應用於這種情境的。   我們先看一下Zepto官方的API文檔:     on 方法還支援在 回呼函數 前傳入一個 [selector] 的值,而這個 [selector] 就是實際需要監聽事件的的元素。 看看實際例子: $('.list').on('click', '.list_items', function (e) {    console.log(e.target.tagName);    console.log(this.tagName);});Demo>> 通過 on 方法把 click 事件代理在 ul.list 元素上,監聽其所有 .list_items 的子項目的點擊操作。 開啟瀏覽器的 Console 看看:   雖然提示每個 .list_items 都有事件監聽,但它們的綁定對象都是指向 ul.list 。   事件代理是通過什麼機制實現的?  這其中的核心思想是 事件冒泡(Event Bubble) 。但在這裡我不打算細說事件冒泡,因為這會是一個很大的話題。Google一下,就能找到有很多相關的介紹了。   下面我們看一下 Zepto 的源碼,它是怎麼去處理這個事件代理的。 方法 on 的實現: 複製代碼  $.fn.on = function(event, selector, data, callback, one){    var autoRemove, delegator, $this = this    if (event && !isString(event)) {      $.each(event, function(type, fn){        $this.on(type, selector, data, fn, one)      })      return $this    }     if (!isString(selector) && !isFunction(callback) && callback !== false)      callback = data, data = selector, selector = undefined    if (isFunction(data) || data === false)      callback = data, data = undefined     if (callback === false) callback = returnFalse     return $this.each(function(_, element){      if (one) autoRemove = function(e){        remove(element, e.type, callback)        return callback.apply(this, arguments)      }       if (selector) delegator = function(e){        var evt, match = $(e.target).closest(selector, element).get(0)        if (match && match !== element) {          evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})          return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))        }      }       add(element, event, callback, data, selector, delegator || autoRemove)    })  }複製代碼  代碼的大致執行流程如下:   由上面的活動圖表可以看出,大部分邏輯都是用來處理 on 方法傳入參數,delegator 函數是事件代理的關鍵。 至於 add 方法,主要是對某些特殊事件進行處理,例如:ready、hover等。當成黑盒去看待,它就等同於原生的 addEventListener 方法。 下面再細分一下 delegator 函數的實現邏輯:   到這裡,大家應該都清楚了。 調用 on 方法進行事件綁定時,只有傳入 [selector] 參數才會實現事件代理。 實際應用中,如何用 JavaScript 原生代碼寫出事件綁定呢?如下: 複製代碼var listElem = document.querySelector('.list');listElem.addEventListener('click', function (e) {    var delegateTarget = this,        fireTarget = e.target,        eventTarget;     if (fireTarget.className !== 'list_items') {        function findParent(elem) {            if (elem === delegateTarget) {                return null;            }            var parent = elem.parentNode;            if (parent.className === 'list') {                return null;            }            if (parent.className === 'list_items') {                return parent;            }            findParent(parent);        }        eventTarget = findParent(fireTarget);    } else {        eventTarget = fireTarget;    }    if (!eventTarget) return false;    console.log('fireTarget: ' + fireTarget.tagName);    console.log('eventTarget: ' + eventTarget.tagName);}, false);

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.