具体谈如何实现JS为句柄添加监听函数之前先看一段代码,算是抛出这个问题。
1<html> 2<head> 3<title>JS为句柄添加监听函数</title> 4 5<script> 6 function message(){ 7 alert("hello!"); 8 } 9</script> 10</head> 11<body> 12 <center> 13 <div onclick="message();"><p>JavaScript</p></div> 14 </center> 15</body> 16</html>
程序代码本身的运行是不会有问题的。不过我的着眼点在于CSS是为了让html文档结构和变现分离,JavaScript着眼于文档结构和行为的分离,上面带按摩并未做到文档结构和行为的彻底分离(div上指明了onclick事件)。
接下来就是如何在js中实现对div添加onclick监听函数。
JS实现行为、结构分离
其实在JavaScript中为DOM元素绑定事件监听函数是一件非常常见的事情,但是这里也有许多bug。各种浏览器对于事件的绑定都提供了很多的方法,但是可靠的只有3种:
传统绑定:
1<html><head> 2 <title>JS为句柄添加监听函数</title> 3 4 <script> 5 function message(message) { 6 alert(message); 7 } 8 9 window.onload = function() { 10 var div = document.getElementById("div"); 11 div.onclick = function(event) { 12 message(event.type); 13 }; 14 } 15 </script></head><body> 16 <center> 17 <div id="div"> 18 <p>JavaScript</p> 19 </div> 20 </center></body></html>
A:传统的绑定方法,非常简单稳定,而且函数体内的this就是正在处理事件的节点。
B:一个元素的事件句柄只能注册一个函数,重复注册会产生覆盖;而且传统的绑定只会在事件冒泡中运行。
Tips:事件冒泡:当一个元素上的事件触发时,同样的事件会在该元素的所有祖先元素中触发。该事件从元素一直冒泡到DOM树的最上层,这一过程称为事件冒泡。
W3C标准绑定:
1<html><head> 2 <title>JS为句柄添加监听函数</title> 3 4 <script> 5 function message(message) { 6 alert(message); 7 } 8 9 window.onload = function() { 10 var div = document.getElementById("div"); 11 //注册监听函数 12 div.addEventListener('click',function(event){ 13 message(event.type+" hello"); 14 },false); 15 //重复注册监听函数 16 div.addEventListener('click',function(event){ 17 message(event.type+" world"); 18 },false); 19 } 20 </script></head><body> 21 <center> 22 <div id="div"> 23 <p>JavaScript</p> 24 </div> 25 </center></body></html>
A、这种绑定方法同时支持事件处理的捕获和冒泡两个阶段;同一元素的同一事件句柄可以注册多个监听函数;而且内部this指向当前元素
Tips:adEventListener中的第三个额参数为false(冒泡获取由里向外)|true(由外向里)
B、我大IE不支持这种注册方法。
IE绑定:
1<html><head> 2 <title>JS为句柄添加监听函数</title> <script> 3 function message(message) { 4 alert(message); 5 } 6 7 window.onload = function() { 8 var div = document.getElementById("div"); 9 //注册监听函数 10 div.attachEvent('onclick', 11 function() { 12 message(window.event.srcElement.innerHTML + ' ' + this.innerHTML + 1); 13 } 14 ); 15 //重复注册监听函数 16 div.attachEvent('onclick', 17 function() { 18 message(window.event.srcElement.innerHTML + ' ' + this.innerHTML + 2); 19 } 20 ); 21 } 22 </script></head><body> 23 <center> 24 <div id="div"> 25 <p>JavaScript</p> 26 </div> 27 </center></body></html>
A:这种绑定方法,可以为同一事件句柄注册多次。
B:IE的事件模型不支持事件捕获;监听函数体内的this指向的不表示当前元素,而且window.event.srcElement指向的是发生事件的节点,而不是当前节点。
除了强烈的吐槽一下IE浏览器之外,我们也只能静下心来寻求一种兼容的方法了,接下来介绍两种方法:
跨浏览器方法一:
1function addEvent(element, type, handler) { 2 if (!handler.$$guid) handler.$$guid = addEvent.guid++; 3 if (!element.events) element.events = {}; 4 var handlers = element.events[type]; 5 if (!handlers) { 6 handlers = element.events[type] = {}; 7 if (element["on" + type]) { 8 handlers[0] = element["on" + type]; 9 } 10 } 11 handlers[handler.$$guid] = handler; 12 element["on" + type] = handleEvent; 13 }; 14 addEvent.guid = 1; 15 16 function removeEvent(element, type, handler) { 17 if (element.events && element.events[type]) { 18 delete element.events[type][handler.$$guid]; 19 } 20 }; 21 function handleEvent(event) { 22 var returnValue = true; 23 event = event || fixEvent(window.event); 24 var handlers = this.events[event.type]; 25 for (var i in handlers) { 26 this.$$handleEvent = handlers[i]; 27 if (this.$$handleEvent(event) === false) { 28 returnValue = false; 29 } 30 } 31 return returnValue; 32 }; 33 34 function fixEvent(event) { 35 event.preventDefault = fixEvent.preventDefault; 36 event.stopPropagation = fixEvent.stopPropagation; 37 return event; 38 }; 39 fixEvent.preventDefault = function() { 40 this.returnValue = false; 41 }; 42 fixEvent.stopPropagation = function() { 43 this.cancelBubble = true; 44 };
参考:http://dean.edwards.name/weblog/2005/10/add-event2/
跨浏览器方法二:
1function addEvent( obj, type, fn ) { 2 if ( obj.attachEvent ) { 3 obj['e'+type+fn] = fn; 4 obj[type+fn] = function(){obj['e'+type+fn]( window.event );} 5 obj.attachEvent( 'on'+type, obj[type+fn] ); 6 } else 7 obj.addEventListener( type, fn, false ); 8 } 9 function removeEvent( obj, type, fn ) { 10 if ( obj.detachEvent ) { 11 obj.detachEvent( 'on'+type, obj[type+fn] ); 12 obj[type+fn] = null; 13 } else 14 obj.removeEventListener( type, fn, false ); 15 }