Rumah  >  Soal Jawab  >  teks badan

javascript - jQuery 嵌套 event 会触发多次的原因?

Html代码如下:

<p id="cover">
  <input type="button" id="inside" value="submit_inside" />
</p>
<p id="cover_js">
   <input type="button" id="inside_js" value="submit_inside_js" />
</p>
<input type="button" id="outside" value="submit_outside" />

jQuery代码如下:

$('#cover').mouseover(function () {
  $('#inside').click(function () {
    alert('multiple times');
  });    
});

$('#cover_js').mouseover(function () {
  document.getElementById('inside_js').onclick = function () {
    alert('just one time');
  }    
});

$('#outside').click(function () {
  alert('just one time');
});

id 为 inside 的按钮的 click 事件被嵌套在 p 的鼠标事件内,而 id 为 outside 的按钮则未嵌套,分别对两个按钮进行多次点击后,inside 按钮绑定的 click 事件会进行累加,而 outside 按钮则仍旧只绑定一个 click 事件。id 为 inside_js 的按钮的 click 事件也被嵌套在 p 的鼠标事件内,但是我用 javascript 实现 click,此时点击多次后,只触发一个 alert。
解决上述问题可以用 unbind 对嵌套 event 进行解绑:

$('#cover').mouseover(function () {
  $('#inside').unbind('click').click(function () {
    alert('multiple times');
  });    
});

但是我想知道为什么 Jquery 嵌套 event 会对事件进行累加,从而导致触发多次呢?

PHPzPHPz2749 hari yang lalu432

membalas semua(5)saya akan balas

  • 怪我咯

    怪我咯2017-04-10 14:25:53

    谢邀,俺正忙着写 Vim 的哲学呢,呵呵~简单说说:

    1. 所有的事件绑定(.click, .mouseover, .hover 等等)都是委托给更底层的 .on 方法的,通过查看其源码得知 .on 最终会调用 $.event.add 方法

    2. 我没有细细去看 $.event 对象的全部内容,只是看到这一行的时候,我似乎有些明悟了。

      1. jQuery 为每一个事件回调函数生成一个 guid,保证它们都是独一无二的(即使函数内容相同)

      2. 如果你继续往后看,会发现这些回调函数都被 push 到一个数组里保存,所以当时间触发的时候,它们也应该是一个个从数组里取出来再执行(或许是为了保证注册在同一事件下不同回调函数的执行顺序,但是像你这样内容一样的回调函数就不好判断了,所以如果你不得不多次绑定内容一样的回调,先解绑)。这就是为什么会产生你问题中效果的原因。

    3. 至于 jQuery 为什么要这样去设计我还没有去求证,我猜是为了更强大的事件管理能力,至于 bind after unbind 这样的副作用也许在 jQuery Team 看来是微不足道的牺牲吧。

    balas
    0
  • 怪我咯

    怪我咯2017-04-10 14:25:53

    jQuery的click方法本来就是调用时绑定一个点击事件回调,这样写可能清晰一点:

    $ele.bind('click',callback);

    你的代码每次mouseover的时候就多绑定了一次事件回调。
    原生的onclick每次赋值都直接覆盖了之前的onclick绑定,所以不推荐这样写。
    你用原生的addEventListener也会得到跟jQuery一样的结果。

    LZ应该多思考一下,你遇到的问题和你回答过的问题一样: http://segmentfault.com/q/1010000000458010 中,你知道解决的方法,但是依然不知道问题产生的原因。

    balas
    0
  • PHP中文网

    PHP中文网2017-04-10 14:25:53

    已经弄懂了这个问题,但是我觉得如果每次绑定事件时直接使用 unbind 方法会使得代码生涩。假设我们的需求确定,只需要内嵌的事件触发一次,我想出下面两种可行的方法:
    1.通过定义一个相反的外层事件解绑内嵌的事件:

    var foo = function () {
        alert('multiple times');
    };
    
    $('#cover').mouseover(function () {
        $('#inside').bind('click', foo);        
    });
    
    $('#cover').mouseout(function () {
        $('#inside').unbind('click', foo);
    });
    

    2.通过定义一个 flag 变量来阻止内嵌事件的累加:

    var flag = true;
    
    var foo = function () {
        alert('multiple times');
    };
    
    $('#cover').mouseover(function () {
        if (flag) {
            $('#inside').bind('click', foo);
            flag = false;
        }
    });
    

    balas
    0
  • 天蓬老师

    天蓬老师2017-04-10 14:25:53

    参考 http://segmentfault.com/q/1010000000455800

    原理其他同学已经回答的很清楚了,这个问题的常见出乎我的意料,似乎有这样的思考回路:“用户先A,然后B,我们要在B的时候做我们的逻辑”,反映到代码上就是类似的代码,bind A, -> bind B, -> myLogic 但多数情况下,其实根本无需关心A,直接在B上绑定事件处理逻辑即可,A一定在B之前发生不意味这代码里你就应该绑定A然后在A的事件里绑B

    实际上,DOM事件绑定这件事,是要极力避免动态绑定/解绑的。从可调试/可维护性的角度来看,事件绑定会变这件事简直就是地狱。DOM绑定要绑的早,绑的少,绑的坚决,不反悔

    万不得已只能解绑的时候,也请用jQuery的event namespacing,或者直接声明解绑的handler是谁,不要unbind整个事件

    balas
    0
  • 大家讲道理

    大家讲道理2017-04-10 14:25:53

    这么做是为了事件多投。W3C和IE有两套关于事件处理的API,都允许事件多投。在前端事件多投很有用处。

    另外xxx.onclick的方式不是标准的绑定事件方式,这种方式只能绑定单个函数。

    balas
    0
  • Batalbalas