一 两种事件类型
其实是事件流的顺序,看下面的html代码当单击链接会发生什么呢?
<html> <head></head> <body onclick="javascript:alert('body')"> <div onclick="javascript:alert('div')"> <a href="#" onclick="javascript:alert('a')">链接</a> </div> </body> </html>
当单击链接时会弹出提示a->div->body,说明单击事件会层层上抛给dom元素。这种有最准确的dom元素开始一直上抛事件源的事件流也叫冒泡。
IE仅仅支持事件的冒泡,在firefox下面还有一种叫事件的捕获,他的过程跟冒泡刚刚相反。
<html> <head> <script> function alterMsg(msg){ return function(){ alert(msg); }; } window.onload=function(){ var oBody = document.body; var oDiv = document.getElementsByTagName("div")[0]; var oHref = document.getElementsByTagName("a")[0]; oDiv.addEventListener("click", alterMsg("div"), true); oBody.addEventListener("click", alterMsg("body"), true); oHref.addEventListener("click", alterMsg("a"), true); } </script> </head> <body> <div> <a href="#">链接</a> </div> </body> </html>
他的顺序会是body->div->a
二 四种绑定方式
前面我们看到了3种绑定方式,其实还有一种IE专用的。4种写法如下:
//跟html代码紧密结合,通用 第一种:<a href="#" onclick="doSomething()">链接</a> //通用 第二种:domObj.onClick = function(){.......} //IE专用 第三种: domObj.attachEvent("onclick",function{}(.....)); //DOM标准,IE不支持 第四种: domObj.addEventListener("click",function{}(.....), true/false);
这4种写法中我们关心5个问题
1. 浏览器之间的通用性
2. 一个dom元素对同一个事件能否绑定多个事件
3. 是否支持冒泡跟捕获
4. this关键字是否指向dom元素
5. Event参数
先来说说第5个问题吧,其实他只跟浏览器有关跟绑定方式无关,只有IE不提供Event参数,而作为window.Event提供。dom标准会为事件函数提供一个Event参数。
至于第4个问题,只有第3种方式,也就是IE事件API它的this指针会指向window对象,其他的都指向当前dom元素.
只有DOM标准才同时支持冒泡跟捕获两种模式,其余都仅支持冒泡
第1,2种其实是一样的也称为传统的绑定方式,他们的通用性最好,但是不支持第2点即多绑。
三 模拟多绑
因为传统方式有最好的通用性,明了的this指针。所以用实现通用的绑定方式且支持多绑相对于其他方式会方便很多。
Dean Edwards的addEvent库就是非常不错的一个方法。建议看的时候画它的数据结构,这样就很容易看懂了
function addEvent(element, type, handler){ //为每一个事件处理函数绑定一个独立的ID if(!handler.$$guid) handler.$$guid = addEvent.guid++; //为元素建立一个事件类型的hash表 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; } //ID计数器 addEvent.guid = 1; function handleEvent(event){ var returnValue = true; //获取事件对象(处理IE与DOM的不同) 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; } // 增加一些IE事件对象缺乏的方法 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; } function removeEvent(element, type, handler){ if(element.events && element.events[type]){ delete element.events[type][handler.$$guid]; } }
四 模拟jQuery的事件写法
在jQuery中事件写法通常是这样的:
$("...").click(function(){ ...... });
对照上面的addEvent这里的参数少了2个,一个是dom元素这个在jQuery对象内部,一个事件类型这里变成了函数名。还有一个问题是事件类型非常多,所以不可能用通常的方式为每个类型定义个函数。像上面的click函数应该就不存在显示的函数定义。