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