非主流童话

风为我动,虽狂亦从容。青龙一点向苍穹。天地谁人争锋?俱无踪,千年沧桑本如是,乱舞随心而动。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

关于事件的重复绑定

Posted on 2012-02-16 20:05  非主流童话  阅读(715)  评论(0)    收藏  举报

      我们知道,事件绑定主要有两种模式:一种是单头独占事件,如果赋值多次,后面的会把前面的覆盖掉。另一种是多头事件,我们可以利用attachEvent/addEventListener 捕获事件(关于addEventListener到底是几个参数,不在本文讨论范围之内^-^)。在多头事件中,我们可以不必关心其他的处理方法,而且对同一个事件,我们可以有多个处理方法,且一般不互相干扰。

      现在如jquery,qwrap这样的前端框架,更会为我们包装好一系列事件处理方法。使用这些框架我们可以很方便的采取bind , delegate方法,为一个或者一组dom绑定/代理事件。  

      但是我们有时或会遇到重复绑定的问题。在程序运行的过程中,我们多次的运行了一个绑定。其结果是我们在事件发生时候,反复地运行事件处理函数。如果这个函数里面包含远程请求等占资源的操作,有时候后果往往是非常严重的。

      设想一个场景,我们有一个主按钮,当它按下的时候,我们才触发复杂的界面绑定。也就是说,我们要为一个按钮绑定一个事件,而这个事件要做的事情,是为一组元素绑定事件。如果我们对这个主按钮的绑定不加以限制。之后的一组元素有可能会做N次同样的动作。

      通过一段代码来演示这个坑的坑容坑貌

 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>演示重复绑定</title>
5 </head>
6 <body>
7 <script type="text/javascript" src="http://s0.qhimg.com/lib/qwrap/102.js"></script>
8 <div style="border:1px;margin:2px;">
9 <span style="padding:3px;"><input type="button" value="安装指令" id="install"/></span>
10 <fieldset>
11 <span>
12 <input type="button" id="fun1" value="左转">
13 <input type="button" id="fun2" value="右转">
14 <input type="button" id="fun3" value="直行1000米">
15 </span>
16 </fieldset>
17 </div>
18 <script>
19 function installCar(){
20 alert("为您安装汽车转向系统......");
21
22 W("#fun1").on("click" , function (){
23 alert("按您的指令,我左转了90度");
24 });
25 W("#fun2").on("click" , function (){
26 alert("按您的指令,我右转了90度");
27 });
28
29 W("#fun3").on("click" , function (){
30 alert("按您的指令,我直行了1000米");
31 });
32 }
33 W("#install").on("click" , installCar());
34 </script>
35 </body>
36 </html>

      如上,当我们反复点击“安装指令”,那么,当我们点击fieldset里面的功能键时候发现,汽车总是原地打转,或是以迅雷不及掩耳盗铃之势跑过辙。

      其实在这种规模上来看,还是比较容易发现问题的。从逻辑上讲,他把装配与响应放在一起。应该尽量避免

      当代码量复杂到一定程度时候,这个错误就有可能被掩盖。这显然是我们不愿意看到的。

      那么,我们迫切地希望能够将这些错误扼杀在摇篮之中。和ququ同学交流之后,得出几种解决方案。

      1.设置标志位。

      绑定之前,判断这个标志位是否为false, 如果不是,不再绑定。为false时,在绑定之后置标志位为true.

      2.在主元素上增加自定义属性。

      每次绑定之前,检查这个属性,如果有,则不再绑定,如果没有,绑定之后要置此自定义属性。这种比第一种的好处在于,不需要关心标志位的作用域

      3.利用前端框架的特性。

      经过测试,在非IE下,qwrap对于指向同一个函数指针的绑定,会做消重处理。这种重复绑定,qw会自动无视。

      而jquery虽不会自动消重。但是提供了一个one方法,运行完毕,自己销毁。

      据小道消息,qwrap也会在近期版本提供与one类似的方法。

      最新发布的qwrap1.1.0版本实现了once方法,可以完成一次操作:文档

      需要注意的是,如果你想使用qwrap自动消重的特性,要求你绑定函数的指针是稳定的。如果代码是这样的,消重是无效的。

  for (var i = 0 ; i < 10 ; ++i){
       W("#clicktest").on("click"  , function (){alert("垂死挣扎!");});
  }

      为此,建议把绑定放到一个稳定的作用域里面,单独命名。如下的写法,在非IE浏览器下,qw就能消重。      

var alertMe = function (){
alert("拨云见日!");
};  
for (var i = 0 ; i < 10 ; ++i){ 
     W("#clicktest").on("click"  , alertMe);
}

     4.每次运行后加上处理函数un到处理函数。 

       以qwrap为例:  

W('#test').click(function() { 
//some code
W(this).un('click', arguments.callee);
});

      将上述代码修改一下,可以更通用地完成运行后卸载。