测试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 级事件,特别是代码提供给别人使用时,绝对不能写!