文档章节

开发中遇到的jQuery 事件处理机制的问题

MrMign
 MrMign
发布于 2013/11/27 13:15
字数 958
阅读 1029
收藏 18

前段时间遇到dom绑定click事件,触发时会重复执行绑定的函数,探究下jQuery事件绑定机制。基于jQuery-1.10.2。

###起因:(对原始问题的一个抽象案例)

<!-- lang: html -->
<html>
<head>
    <title>test click</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">    </script>
</head>
<body>

<a href="#" id="click1" class="clickc">click1</a> <br>
<a href="#" id="click2" class="clickc">click2</a> <br>
<a href="#" id="click3" class="clickc">click3</a> <br>
<a href="#" id="click4" class="clickc">click4</a> <br>
<a href="#" id="click5" class="clickc">click5</a> <br>

<hr>
<a href="#" id="click" class="target">click</a>

<script type="text/javascript">
var count = 0;
function clickEvent()
{
    console.log("click --" + count);
    console.log("---------------");
    count ++;
}
// $('#click1').click(function(){
//     $("#click").click(clickEvent);
// })
// $('#click2').click(function(){
//     $("#click").click(clickEvent);
// })
// $('#click3').click(function(){
//     $("#click").click(clickEvent);
// })
// $('#click4').click(function(){
//     $("#click").click(clickEvent);
// })
// $('#click5').click(function(){
//     $("#click").click(clickEvent);
// })

$('.clickc').click(function(){
    //$('.target').off();
    $('.target').click(clickEvent);
    // console.log(count);
    // count++;

    // var c = document.getElementById('click');
    // c.addEventListener('click', clickEvent);

})
$('.clickclass').one('click', clickEvent);
</script>
</body>
</html>

####问题描述

  1. 点击click[1-5]中的任意一个,接着点击最下面的 click, 这时会在控制台输出,此时clickEvent只执行一次【F12, 打开chrome调试工具,找到console】
  2. 点击click[1-5]中的任意一个,此时点击最下面的 click,此时在console会有2个输出,说明 clickEvent 被执行了2 次
  3. 如果不刷新,一直重复上面 的操作,最终的结果就是 点击几次click[1-5],点击click时就会执行几次clickEvent函数

###原因分析 jQuery的事件绑定机制里用数组来保存事件,如果对同一元素进行重复绑定,不会覆盖之前已经绑定的事件,只是把新的绑定事件再push到保存事件的数组中,当事件触发时就会循环执行数组中的事件。

####jQuery源码中add函数进行元素事件的绑定。

<!-- lang: js -->
add: function( elem, types, handler, data, selector ) {
    ......
    if ( !(handlers = events[ type ]) ) {
    handlers = events[ type ] = []; //事件处理队列
        ....
    }
    ......
    // 添加到事件列表中, 
if ( selector ) {
    handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
    handlers.push( handleObj );
}
    ......
}

####jQuery源码中dispatch进行事件处理

<!-- lang: js -->
dispatch: function( event ) {
    ....
    handlerQueue = jQuery.event.handlers.call( this, event, handlers );//拿到事件队列
    i = 0;
    while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
       event.currentTarget = matched.elem;
        j = 0;
        while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
    // Triggered event must either 1) have no namespace, or
    // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
            if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {

                event.handleObj = handleObj;
                event.data = handleObj.data;

        ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
                .apply( matched.elem, args );
        ....
            }
        }
    }
    return event.result;
}

在通过$("").click()$("").on("click",function)进行事件注册时,都会调用add进行事件注册。

###解决方法 通过以上分析可以知道,如果对同一元素进行多次事件绑定,当触发事件的时候就会执行事件队列里的所有的处理函数。因此可以有以下几种方法解决:

  1. 对元素只进行一次事件绑定。

  2. 对元素进行事件绑定前,先调用off()方法,解决该元素的所有绑定事件处理函数。

    <!-- lang: js -->

    $(elem).off(); $(elem).click(func);

  3. 使用原生js进行事件注册,原生的js对事件没有缓存机制,进行多次绑定只有最后一次是有效的,后面的会覆盖前面的事件处理函数。

    <!-- lang: js -->

    var elem = document.getElementById('click'); elem.addEventListener('click', func);

###【补充】Learning jQuery, 4th Edition p77 在jQuery中,当一个处理函数绑定到事件上时,之前绑定的处理函数依然有效。可以通过调用 .off()方法一次性解除所有的绑定。

p78 如果想在事件处理函数第一次触发后就会解除绑定,可以用.one()来绑定函数。

.one()来绑定函数,事件处理函数会接着执行。

<!-- lang: js -->
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );
}
on: function( types, selector, data, fn, /*INTERNAL*/ one ){
    ....
    if ( one === 1 ) {
    origFn = fn;
    fn = function( event ) {
        // Can use an empty set, since event contains the info
        jQuery().off( event );
        return origFn.apply( this, arguments ); //直接调用并返回
    };
    // Use same guid so caller can remove using origFn
    fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
return this.each( function() {
	jQuery.event.add( this, types, fn, data, selector );
});
}

© 著作权归作者所有

共有 人打赏支持
MrMign
粉丝 89
博文 90
码字总数 18583
作品 0
深圳
程序员
私信 提问
加载中

评论(9)

_金角大王_
_金角大王_
我 @ 你了
_金角大王_
_金角大王_
@你了
MrMign
MrMign

引用来自“疯狂的舌头”的评论

当然是方法最后一行啊。

不行啊
MrMign
MrMign

引用来自“疯狂的舌头”的评论

当然是方法最后一行啊。

好,我试下。
_金角大王_
_金角大王_
当然是方法最后一行啊。
MrMign
MrMign

引用来自“疯狂的舌头”的评论

你先试试是否好使,没记错的话应该是ie6的一个bug。

要在哪里return false呀?
_金角大王_
_金角大王_
你先试试是否好使,没记错的话应该是ie6的一个bug。
MrMign
MrMign

引用来自“疯狂的舌头”的评论

return false; 直接解决

什么意思呀?请指教
_金角大王_
_金角大王_
return false; 直接解决
第78天:jQuery事件总结(一)

jQuery事件总结(一)   现在就一点一点积累自己的知识体系,记录自己学到的和自己所理解的jQuery。   JavaScript和HTML之间的交互式通过用户和浏览器操作页面时引发的事件机制来处理的。...

半指温柔乐
2017/11/08
0
0
在 ASP.NET 的 UpdatePanel 中使用 jQuery

简述 我在开发一个 ASP.NET Webforms 应用程序时, 发现 JQuery 会在我同时使用的 UpdatePanel 的时候失效! 在我深入研究后发现,原来是UpdatePanel 的局部刷新事件在调用完成后移除了JQuery ...

oschina
2013/06/04
626
0
jQuery系列 第一章 jQuery框架简单介绍

第一章 jQuery框架简单介绍   1.1 jQuery简介   jQuery是一款优秀的javaScript库(框架),该框架凭借简洁的语法和跨平台的兼容性,极大的简化了开发人员对HTML文档,DOM,事件以及Ajax的...

文顶顶水水
2018/04/09
0
0
jQuery的发展史,你知道吗?

每天多学一点知识,就少写一行代码 2006年1月,jQuery的第一个版本面世,至今已经有6年多了(注:这个时间点是截止至出书时间)。虽然过了这么久,但它依然以其简洁、灵活的编程风格让人一见...

生气的散人
2014/01/21
507
0
从零开始学 Web 之 jQuery(一)jQuery的概念,页面加载事件

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:http://www.cnblogs.com/lvonve/ CSDN...

fengdaoting
2018/07/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊flink的KvStateRegistryGateway

序 本文主要研究一下flink的KvStateRegistryGateway KvStateRegistryGateway flink-1.7.2/flink-runtime/src/main/java/org/apache/flink/runtime/jobmaster/KvStateRegistryGateway.java pu......

go4it
19分钟前
2
0
Java springboot B2B2C o2o多用户商城 springcloud架构 (十四)在springboot中用redis实现消息队列

准备阶段 安装redis,可参考我的另一篇文章。 java 1.8 maven 3.0 idea 环境依赖 创建一个新的springboot工程,在其pom文件,加入spring-boot-starter-data-redis依赖: <dependency> ...

itcloud
26分钟前
1
0
云计算的2018年都有什么变化?

2018 年,区块链(Blockchain)在技术圈的风头一时无两,连此前大红大紫的人工智能(AI)都稍逊风骚,云计算圈则奋十年之余烈,完善产品,深耕行业,让越来越多的行业客户接受和实施云计算。回顾...

linuxCool
26分钟前
4
0
[activiti6]调用WebService几个坑

[activiti6]几个报错解决 运行test webservice org.activiti.engine.ActivitiException: no default process engine availableat org.activiti.engine.impl.test.PluggableActivitiTestCas......

Danni3
31分钟前
1
0
一张图看懂SQL的各种Join用法

下图展示了 LEFT JOIN、RIGHT JOIN、INNER JOIN、OUTER JOIN 相关的 7 种用法。 具体分解如下: 1、INNER JOIN(内连接)   SELECT FROM Table_A AINNER JOIN Table_B BON A.Key = B.Key 2、L......

dragon_tech
54分钟前
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部