我们知道,事件绑定主要有两种模式:一种是单头独占事件,如果赋值多次,后面的会把前面的覆盖掉。另一种是多头事件,我们可以利用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);
});
将上述代码修改一下,可以更通用地完成运行后卸载。
浙公网安备 33010602011771号