JavaScript事件代理入门

事件代理(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);

Demo>>

时间: 2024-11-08 18:35:15

JavaScript事件代理入门的相关文章

深入分析Javascript事件代理_基础知识

很久很久以来,总感觉事件发生与事件代理到之间没什么鸟区别. 最近,又看了一下,感觉区别其实真不大!看怎么理解吧. 要搞清楚什么是事件代理,就需要先搞清楚什么是代理. 从商业角度来讲,代理就是:我有货,你没货,但丫我没时间.没精力全部卖掉,而你一天闲的蛋疼,只剩下时间了.于是,我委托你帮我买,然后哥给你提成.这个过程中,你实际上相当于也有了货. OK,怎么从字面来理解事件代理一词的含义?后文有讲. 一 先看一个真实的,新手绑定onclik事件的例子 如果按照之前的我,我会怎么给每一个li标签,添加

浅析JavaScript的事件代理和委托

在javasript中delegate这个词经常出现,看字面的意思,代理.委托.那么它究竟在什么样的情况下使用?它的原理又是什么?在各种框架中,也经常能看到delegate相关的接口.这些接口又有什么特殊的用法呢?这篇文章就主要介绍一下javascript delegate的用法和原理,以及Dojo,jQuery等框架中delegate的接口. JavaScript事件代理 首先介绍一下JavaScript的事件代理.事件代理在JS世界中一个非常有用也很有趣的功能.当我们需要对很多元素添加事件的

浅析javascript中的事件代理_javascript技巧

本文的主要内容是根据前不久面试某家公司Web前端开发岗位,面试时做的一道数组去重问题的解题思路进行整理的,分享给大家. 题目本身很简单:一个ul中有一千个li,如何给这一千个li绑定一个鼠标点击事件,当鼠标点击时alert出这个li的内容和li的位置坐标xy, <ul id="ulItem"> <li id="li1">1</li> <li id="li2">2</li> <li

JavaScript通过事件代理高亮显示表格行的方法

  本文实例讲述了JavaScript通过事件代理高亮显示表格行的方法.分享给大家供大家参考.具体实现方法如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

JavaScript的事件代理和委托实例分析

 在javasript中delegate这个词经常出现,看字面的意思,代理.委托.在各种框架中,也经常能看到delegate相关的接口.这些接口又有什么特殊的用法呢?这篇文章就主要通过实例介绍一下javascript delegate的用法和原理.     在JavaScript中,经常会碰到要监听列表中多项li的情形,假设我们有一个列表如下:   代码如下: <ul id="list"> <li id="item1">item1</l

javascript中的事件代理初探

 本文仅仅起到一个抛砖引玉的作用,让大家了解到事件代理的原理而已,jquery中的live和delegate实现要复杂的多 事件在javascript中一直是最强大的对象之一.javascript提供了addEventListener和attachEvent两个方法来为DOM节点绑定事件,jquery作了进一步封装,提供了兼容各个浏览器的bind方法.现在来看,这种传统的事件绑定方式存在着以下不足:   1.可能需要绑定很多的EventHander.   假如页面中某个表有100行,现在必须为每

javascript中的事件代理初探_javascript技巧

事件在javascript中一直是最强大的对象之一.javascript提供了addEventListener和attachEvent两个方法来为DOM节点绑定事件,jquery作了进一步封装,提供了兼容各个浏览器的bind方法.现在来看,这种传统的事件绑定方式存在着以下不足: 1.可能需要绑定很多的EventHander. 假如页面中某个表有100行,现在必须为每一行绑定一个click事件.那么就必须绑定100个EventHandler,这对页面性能来说有着极大的负担,因为需要创建更多的内存来

JavaScript事件---事件入门

原文:JavaScript事件---事件入门 内容提纲: 1.事件介绍 2.内联模型 3.脚本模型 4.事件处理函数   JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击.当用户执行某些操作的时候,再去执行一系列代码.   一.事件介绍 事件一般是用于浏览器和用户操作进行交互的.最早是IE和Netscape Navigator中出现,作为分担服务器端运算负载的一种手段.直到几乎所有的浏览器都支持事件处理.而DOM2级规范开始尝试以一种复合逻辑的方式标准化DOM事件

JavaScript的事件代理和委托实例分析_javascript技巧

在JavaScript中,经常会碰到要监听列表中多项li的情形,假设我们有一个列表如下: 复制代码 代码如下: <ul id="list">   <li id="item1">item1</li>   <li id="item2">item2</li>   <li id="item3">item3</li>   <li id="