测试DOM0级事件和DOM2级事件的堆叠

1. 问题

如果大家看过北风网CJ讲师的Javascript视频教程,就可以看到其封装了一个很强的事件添加和删除函数,如下所示

 1  function addEvent(obj, evtype, fn) {
 2     //符合W3C的事件模型
 3     if (obj.addEventListener) {
 4         obj.addEventListener(evtype,fn,false);
 5         return obj;
 6     }
 7         
 8     //IE或Opera浏览器
 9     if (!obj.functions) obj.functions={};
10     if (!obj.functions[evtype])
11         obj.functions[evtype] = [];
12         
13     var functions=obj.functions[evtype];
14     for (var i=0;i<functions.length;i++) {
15         if (functions[i] === fn) return obj;
16     }
17     functions.push(fn);
18     
19     
20     if (typeof obj["on"+evtype]=="function") {
21         if (obj["on"+evtype]!=handler)
22             functions.push(obj["on"+evtype]);
23     }
24     obj["on"+evtype]=handler;
25     return obj;    
26 }
27 
28 
29  function delEvent(obj,evtype,fn) {
30     if (obj.removeEventListener) {
31         obj.removeEventListener(evtype,fn,false);
32         return obj;
33     }
34     var fns=obj.functions || {};
35     fns=fns[evtype] || [];
36     for (var i=0;i<fns.length;i++) {
37         if (fns[i]==fn) {
38             fns.splice(i,1);
39             return obj;
40         }
41     }
42 }

它因为各种原因,没有采用IE的DOM2级事件添加函数attachEvent,以期达到非常完美的兼容性,据说可以到 IE5.5 或以下。分析代码页可以看到,它通过给DOM元素添加一个以 on + ‘type’ 为名的属性,其对应的值是一个处理函数的数组,本质来说就是仅仅利用 DOM0级提供的接口 elem.onclick = function () { ...} 来添加事件。

一直以来没有发现什么问题,直到给别人使用时,发现IE6~8通过该封装函数 addEvent 添加的 load 事件并没有执行,调试代码发现别人在 JS 中通过 window.onload 赋了一个处理函数,顿时明白这种 addEvent 封装的一个大Bug,原因很简单,通过DOM0级提供的事件接口多次添加时,后面的函数会覆盖前面的,而这种 addEvent 本质上就是一个 DOM0级函数,所以肯定会被后面的覆盖。

也就是说, addEvent 是假定网页JS代码中不使用 DOM0级添加事件。

 

2. 构建测试页面和代码

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 3 <head>
 4     <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
 5     <script src="dom.event.js"></script>
 6     <title>测试DOM0级事件和DOM2级事件的堆叠</title>
 7 </head>
 8 <body>
 9     <textarea name="have" id="output" cols="30" rows="10"></textarea>
10 
11     <button id="testBtn">测试</button>
12 
13     <script type="text/javascript">
14         var btn = document.getElementById('testBtn');
15         var output = document.getElementById('output');
16 
17 
18         // 标准的 DOM2 级事件添加方法
19         // if (btn.addEventListener) {
20         //     btn.addEventListener('click', function () {
21         //         output.value += 'DOM 2 Level Click\r\n';
22         //     });
23         // } else {
24         //     btn.attachEvent('onclick', function () {
25         //         output.value += 'DOM 2 Level Click\r\n';
26         //     })
27         // }
28 
29         // 使用封装的 addEvent 函数
30         // addEvent(btn, 'click', function () {
31         //     output.value += 'DOM 2 Level Click\r\n';
32         // });
33 
34         btn.onclick = function () {
35             output.value += 'DOM 0 Level Click 111111\r\n';
36         }
37 
38         btn.onclick = function () {
39             output.value += 'DOM 0 Level Click 222222\r\n';
40         }
41 
42     </script>
43 </body>
44 </html>

测试1:点击测试按钮,可以看到textarea 中只是输出 DOM 0 Level Click 22222 信息,也就是后面添加的事件覆盖了之前添加的,跟变量赋值是一个道理!!

测试2:将 DOM2 级事件添加的代码反注释掉,打开网页点击测试按钮,可以看到会输出 DOM 2 Level Click 和 DOM 0 Level Click 22222,也就是说DOM0 级事件和 DOM2 级添加的事件都会处理,DOM0级并不会覆盖 DOM2 添加的事件处理函数。

测试3: 将 DOM2 级事件保持注释状态,反注释掉 addEvent 部分, 可以看到只会输出 DOM 0 Level Click 22222。

测试4: 在测试3 的基础上, 注释掉后面两个 DOM0 级添加事件的代码,以测试 addEvent 的输出,可以看到它输出 DOM 2 Level Click, 从而证明测试3中其被覆盖的结论。

 

3. 总结

  1) DOM0 级事件和 DOM2 级事件可以共存;

  2) 尽量不要写 DOM0 级事件,特别是代码提供给别人使用时,绝对不能写!

posted @ 2013-08-23 08:12  风影无忌  阅读(2410)  评论(0编辑  收藏  举报