jQuery的事件绑定与事件委托

   在用jQuery的事件绑定时,会用到on()、bind()、live()、 delegate()这几个方法,但对他们的区别缺从未注意过,现稍总结一下,如有错误,欢迎指正。

      参考文档:
http://blog.csdn.net/xxd851116/article/details/8646899
http://www.jb51.net/article/57827.htm
http://www.jb51.net/article/67166.htm
http://api.jquery.com/on/
http://api.jquery.com/delegate/
http://api.jquery.com/bind/
http://api.jquery.com/live/
http://www.cnblogs.com/aaronjs/p/3481075.html
http://blog.jquery.com/2016/05/20/jquery-1-12-4-and-2-2-4-released/
http://blog.jquery.com/2016/06/09/jquery-3-0-final-released/

DOM树

      我们先种一颗DOM树,来说明接下来将要讲到的事件冒泡,图片引用自:
http://blog.csdn.net/xxd851116/article/details/8646899
jQuery的事件绑定与事件委托_第1张图片

在操纵DOM的语境中,document是根节点。

事件冒泡

      该小节内容均引自:
http://blog.csdn.net/xxd851116/article/details/8646899

当我们点击一个a链接时,其触发了链接元素的单击事件,该事件会引发所有已绑定到该元素的单击事件上的函数的执行——这就是事件触发

      如下代码,一个用户单击操作会触发alert函数的执行。

$('a').bind('click', function() { alert("That tickles!") });  

      该事件触发过程用图片表示如下,图片引用自:
http://blog.csdn.net/xxd851116/article/details/8646899
jQuery的事件绑定与事件委托_第2张图片

而在上图执行之后,click事件会接着向树的根方向传播,广播到父元素,然后是每级的祖先元素,只要是它的某个后代元素上的单击事件被触发,事件就会传给它————这就是事件冒泡

      整个过程如下图所示,图片引用自:
http://blog.csdn.net/xxd851116/article/details/8646899
jQuery的事件绑定与事件委托_第3张图片

官方说明

      明白了事件冒泡的概念,我们正式进入jQuery的事件绑定与事件委托(也成事件代理)的讲解中。

      首先我们从jQuery的官网上看一下关于这4个方法的说明:

live():jQuery1.7之后弃用 (故本文不再讲解这个方法)
bind(): 动态添加的元素无法使用该方法绑定事件
delegate():jQuery1.7之后被on()取代
on(): jQuery1.7之后引入,支持事件绑定的全部功能。

      此外,jQuery官网上还说明的一点是:

In Internet Explorer 8 and lower, a few events such as change and submit do not natively bubble but jQuery patches these to bubble and create consistent cross-browser behavior.(引用自:
http://api.jquery.com/on/ ‘Direct and delegated events’)

      翻译过来就是:

在IE8和之前的版本中,少数事件如change、submit不支持原生的事件冒泡,但是jQuery为它们模拟了事件冒泡以此创建了一致的、跨浏览器的行为。

      换句话说:jQuery模拟了所有的事件冒泡,使所有事件到支持事件冒泡并兼容所有浏览器

bind

bind()的用法

      bind()的用法如下:

//绑定单个事件
$( "#foo" ).bind( "click", function() {
  alert( "User clicked on 'foo.'" );
});
//绑定多个事件
$( "#foo" ).bind( "mouseenter mouseleave", function() {
  $( this ).toggleClass( "entered" );
});

bind()存在的问题

      我们用一个能够看出bind()问题的代码来说明

$("div").bind("click", function () {
     alert($(this).text());
 })

      这段代码的作用是:扫描整个html文档找出所有的$(‘div’)元素,并把alert函数绑定到每个元素的click事件上。

      从这个代码中,我们可以发现bind()有如下问题:

    1、这里用到了隐式迭代,如果匹配到的元素特别多的时候,比如页面中有100个div元素,就得执行绑定100次。对于大量元素来说,影响到了性能。但是如果是id选择器,因为id唯一,用bind()方法就很快捷了。
    2、bind()只能给调用它的时候已经存在的元素绑定事件,对于动态添加的元素无法使用该方法绑定事件。
    3、官网建议使用on()来替代bind()

delegate

      上述bind()的前2个问题,用delegate可以解决。

delegate()的用法

      delegate()的用法如下:

$('#container').delegate('a', 'click', function() { alert("That tickles!") }); 

      这段代码的作用是:扫描文档查找$(‘#container’),并使用click事件和’a’这一CSS选择器作为参数把alert函数绑定到$(‘#container’)上。任何时候只要有事件冒泡到$(‘#container’)上,它就查看该事件是否是click事件,以及该事件的目标元素是否与CCS选择器(即’a’元素)相匹配。如果两种检查的结果都为真的话,它就执行函数。

delegate()的特点

    1、直接将目标元素选择符("a")、事件("click")及处理程序与“委托方”$("#container")绑定,不额外收集元素、事件传播路径缩短、语义明确;
    2、支持链式调用,即支持如$("table").find("#info").delegate(...)的代码来精确控制;
    3、使用事件委托时,如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播,那么事件委托就会失效。

使用delegate()而不是bind()的原因

1、为了把处理程序附加到可能还未存在于DOM中的DOM元素之上。因为bind是直接把处理程序绑定到各个元素上,它不能把处理程序绑定到还未存在于页面中的元素之上。

2、如果你运行了$(‘a’).bind(…),而后新的链接经由AJAX加入到了页面中,则你的bind处理程序对于这些新加入的链接来说是无效的。而另一方面delegate则是被绑定到另一个祖先节点上,因此其对于任何目前或是将来存在于该祖先元素之内的元素都是有效的。

3、或者为了把处理程序附加到单个元素上或是一小组元素之上,监听后代元素上的事件而不是循环遍历并把同一个函数逐个附加到DOM中的100个元素上。把处理程序附加到一个(或是一小组)祖先元素上而不是直接把处理程序附加到页面中的所有元素上,这种做法带来了性能上的好处。

(引用自:http://blog.csdn.net/xxd851116/article/details/8646899)

on

      通过查看jQuery的源代码(我查看的版本是1.12.4),可以发现无论bind()还是delegate()都是通过on()方法实现的,只是参数不同,如下图:
jQuery的事件绑定与事件委托_第4张图片

      on()的官方API描述如下:

.on( events [, selector ] [, data ][, handler])

官网建议从jQuery1.7开始,尽量使用on()方法来绑定事件,可以不必再使用delegate()或bind()方法了。

直接绑定和事件委托

      我们直接看官网对直接绑定和事件委托的介绍

      大多数浏览器的事件冒泡,都是从文档的最深、最内层发生事件的元素(也称为event target——事件目标),一路向上到达body或document元素上。在IE8和之前的版本上,少数事件例如change、submit不支持原生的事件冒泡,但jQuery模拟并创建了一致的、跨浏览器的事件冒泡行为。

      

      如果on()方法的selector 参数为空,事件处理程序就被称为直接绑定。每当在被绑定元素上(如下例中被绑定的document元素,译者注)发生事件时,无论这个事件发生在这个元素上还是从内层元素经冒泡而来,该处理程序都会被调用。

      并且,如果on()方法的selector 参数为空,它与bind()方法相同——只能绑定页面已有元素的事件。

      下面我们用代码说明什么是直接绑定

<body>
<div>
        这是一个div
        <button>点击button>
div>
<script type="text/javascript">
        $(function(){
            //将click事件绑定在document对象上,
            //页面上任何元素发生的click事件都冒泡到document对象上得到处理
            //从而调用事件处理程序
            //本例为了简单绑定到了document,实际上绑定到那个元素上都有类似效果
            $(document).on("click",function(e){
               console.log(e.target.tagName + " is clicked")
            })  
        })
    script>
body>

      页面执行效果如下图:
jQuery的事件绑定与事件委托_第5张图片

      如果on()方法的selector 参数不为空,事件处理程序就被称为委托。当事件直接发生在被绑定的元素上(如下例中绑定到document上,译者注)时,该程序不会被调用,而只有当事件发生在与选择器匹配的内部元素上(如下例中click事件发生在button上,译者注)时,才会调用该程序。

      

jQuery的事件委托是将事件从事件的发生者(即event target,译者注)一直向上冒泡到绑定了事件处理程序的元素上(例如从最内层元素向上冒泡到最外层元素),并为冒泡“沿路”过程中匹配的所有选择器执行事件处理程序。

      下面我们用代码说明什么是委托

<body>
    <div>
        这是一个div
        <button>点击button>
    div>
    <script type="text/javascript">
        $(function(){
            //将click事件绑定在document对象上,
            //并传入第二个可选参数:selector
            //当事件冒泡到document对象时,检测事件的target,
            //如果与传入的选择符(这里是button)匹配,就调用事件处理程序即触发事件,否则不触发。
            $(document).on("click","button",function(e){
                console.log(e.target.tagName + " is clicked")
            })
        })
    script>
    body>

      面执行效果如下图:
jQuery的事件绑定与事件委托_第6张图片

事件处理程序只会被绑定到当前已被选定的元素上(如上例中的document元素上,译者注),这些元素在调用on()方法时必须已经存在——同bind()。

为了确保这些元素已经存在并能被选中,请将代码放置到标签之前或者在$(function(){ ... })内执行事件绑定。或者,使用事件委托来绑定事件处理程序。

      

事件委托的优点
      1、能处理来自于内层子元素的事件,这些内层子元素是在稍后时间里被添加的文档中的。通过选择一个在绑定事件委托处理程序时肯定存在的元素,就可以使用事件委托来避免 需要频繁的添加和删除事件处理程序的操作。
      这个元素可以是MVC模式下的一个视图容器元素或者是document元素(如果事件处理程序需要监听文档中的全部冒泡事件)。在加载任何html代码之前,document元素已经在html文档的头部存在了,所以无需等待dom ready就可以在document这个元素上绑定事件了,这是安全可靠的。

      2、除了可以处理尚未创建的内层子元素的事件,事件委托的另一个优点是:当需要监听很多元素时,事件委托的性能更好。

例如:有一个1000行数据的表格,下面的代码就是将一个事件处理程序绑定到这1000个元素上,事件绑定需要执行1000次。

$( "#dataTable tbody tr" ).on( "click", function() {
  console.log( $( this ).text() );
});

      事件委托方式(如下代码)是将事件处理程序绑定到一个元素上,即tbody上,事件只需向上冒泡一级(从被点击的tr元素到tbody元素)。事件只需绑定一次,然后采用事件冒泡来触发事件。

$( "#dataTable tbody" ).on( "click", "tr", function() {
  console.log( $( this ).text() );
});

以上内容翻译自:http://api.jquery.com/on/ 《Direct and delegated events》一节

总结

1.选择器匹配到的元素比较多时,不要用bind()迭代绑定
2.用id选择器时,可以用bind()
3.需要给动态添加的元素绑定时,用delegate()或者on()
4.用delegate()和on()方法,dom树不要太深
5.尽量使用on()来代替delegate()和bind()
6.在需要为较多的元素绑定事件的时候,优先考虑事件委托,可以带来性能上的好处
7.bind()和delegate()都是通过on()方法实现的,只是参数不同——当on()的selector参数为空时,其效果完全等同于bind();当selector参数不为空时,其效果完全等同于delegate();
8.使用.on()方法时,事件只会绑定到$()函数的选择符表达式匹配的元素上(上面例子中,为了简单绑定到了document),因此可以精确地定位到页面中的一部分,从而减少事件冒泡的开销。

附 关于各版本对浏览器的兼容

引用jQuery官网的原话说明一下各版本对浏览器的兼容问题

1.x 版本支持 IE 6/7/8, 2.x 版本不支持 IE 6/7/8

1.12.4下载地址:
https://code.jquery.com/jquery-1.12.4.js
https://code.jquery.com/jquery-1.12.4.min.js

2.2.4下载地址:
https://code.jquery.com/jquery-2.2.4.js
https://code.jquery.com/jquery-2.2.4.min.js

(引用自:http://blog.jquery.com/2016/05/20/jquery-1-12-4-and-2-2-4-released/ )

1.12和2.2版本仍会提供重要补丁,但不会新增功能或做重大修改。3.x版本将是jQuery未来的主流版本,如果仍想在IE6-8上使用jQuery,请使用1.12的最新版本

(引用自:http://blog.jquery.com/2016/06/09/jquery-3-0-final-released/)

你可能感兴趣的:(jquery)