Javascript 跨浏览器绑定事件

Dean Edwards大牛的跨浏览器addEvent()设计

跨浏览器跨的就是执行 W3C 标准的浏览器,如火狐、chrome、safari 和 执行自己标准的 IE。W3C 的事件绑定方法 addEventListener 很完善,而 IE 的事件绑定方法 attachEvent 问题很多,接下来的这个方法就干脆将这两个方法都抛弃掉。

Dean Edwards在文章中提到了该设计的几个优点:

it performs no object detection  不执行对象检测。

it does not use the addEventListener/attachEvent methods  不使用addEventListener/attachEvent,因为这两个方法分别由不同的浏览器支持,使用时候需要判断 。

it keeps the correct scope (the this keyword)  保持正确的作用域,即this关键字 。

it passes the event object correctly  正确的传递event对象 。

it is entirely cross-browser (it will probably work on IE4 and NS4)  保证浏览器兼容性,甚至支持IE4和NetSape4(2005年) 。

and from what I can tell it does not leak memory  不会出现内存泄漏。

<html
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
//事件添加方法
function addEvent(element, type, handler) {
    // assign each event handler a unique ID
    // 为传入的每个事件初始化一个唯一的id
    if (!handler.$$guid) handler.$$guid = addEvent.guid++;   //下文:addEvent.guid = 1;
    // create a hash table of event types for the element
    // 给element维护一个events属性,初始化为一个空对象。  
    // element.events的结构类似于 { "click": {...}, "dbclick": {...}, "change": {...} }  
    // 即element.events是一个对象,其中每个事件类型又会对应一个对象
    if (!element.events) element.events = {};
    // create a hash table of event handlers for each element/event pair
    // 试图取出element.events中当前事件类型type对应的对象,赋值给handlers
    var handlers = element.events[type];
    if (!handlers) {
        handlers = element.events[type] = {};
        //如果handlers是undefined,则初始化为空对象

        // store the existing event handler (if there is one)
        // 如果这个element已经有了一个方法,例如已经有了onclick方法
        // 就把element的onclick方法赋值给handlers的0元素,此时handlers的结构就是:
        // { 0: function(e){...} }
        // 此时element.events的结构就是: { "click": { 0: function(e){...} },  /*省略其他事件类型*/ } 
        if (element["on" + type]) {
            handlers[0] = element["on" + type];
        }
    }
    // store the event handler in the hash table
    // 把当前的事件handler存放到handlers中,handler.$$guid = addEvent.guid++; addEvent.guid = 1; 肯定是从1开始累加的
    // 因此,这是handlers的结构就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... }
    handlers[handler.$$guid] = handler;
    // assign a global event handler to do all the work
    // 下文定义了一个handleEvent(event)函数
    // 将这个函数,绑定到element的type事件上。  说明:在element进行click时,将会触发handleEvent函数,handleEvent函数将会查找element.events,并调用相应的函数。可以把handleEvent称为“主监听函数”
    element["on" + type] = handleEvent;
}
// a counter used to create unique IDs
addEvent.guid = 1;
//主监听函数
function handleEvent(event) {
    // grab the event object (IE uses a global event object)
    // 在IE中,event需要通过window.event获取
    event = event || fixEvent(window.event); // fixEvent 使 IE 事件对象与 W3C 标准同步
    // get a reference to the hash table of event handlers
    // 根据事件类型在events中获取事件集合(events的数据结构,参考addEvent方法的注释)
    var handlers = this.events[event.type];
    // 注意!注意!  这里的this不是window,而是element对象,因为上文 element["on" + type] = handleEvent;
    // 所以在程序执行时,handleEvent已经作为了element的一个属性,它的作用域是element,即this === element
    // execute each event handler
    // 循环执行handlers集合里的所有函数    另外,这里执行事件时传递的event,无论在什么浏览器下,都是正确的
    for (var i in handlers) {
        this.$$handleEvent = handlers[i];
        this.$$handleEvent(event);
        //此处为何要把handlers[i]赋值给this.$$handleEvent,然后在执行呢?
        //而不是直接执行handlers[i](event)?
        //跟内存泄漏有关?
        //我也没看明白,大家自己思考的,知道的可以分享给大家。
    }
}
// Dean Edwards 提供的代码里没有这个函数,是我自己添加的:使 IE 事件对象与 W3C 标准同步,这样在以后就可以使用相同的名称了
function fixEvent(evt) {
    //触发事件的对象
    evt.target = evt.srcElement;
    //relatedTarget
    if (evt.type == "mouseover") {
        evt.relatedTarget = evt.fromElement;
    } else if ("mouseout" == evt.type) {
        evt.relatedTarget = evt.toElement;
    }
    
    //停止事件冒泡
    evt.stopPropagation = function () {
        evt.cancelBubble = true;
    };
    //阻止浏览器默认行为
    evt.preventDefault = function () {
        evt.returnValue=false;
    };
    //这里我们就当作相等,这两对属性还是有点差别的,下面标注了一个链接,可以跳过去看看
    evt.layerX = evt.offsetX;
    evt.layerY = evt.offsetY;
    //鼠标在页面上的位置,IE 没有可替代属性,不过可以通过计算得到
    evt.pageX = evt.clientX + document.documentElement.scrollLeft;
    evt.pageY = evt.clientY + document.documentElement.scrollTop;
    return evt;
}
//移除函数事件
function removeEvent(element, type, handler) {
    // delete the event handler from the hash table
    // 循环element.events[type],根据handler的唯一的id,进行delete
    if (element.events && element.events[type]) {
        delete element.events[type][handler.$$guid];
    }
}
</script>
<script type="text/javascript">
window.onload = function() {
var oDiv = document.getElementById("oDiv");
    addEvent(oDiv, "click", click1);
    addEvent(oDiv, "click", click2);
    //removeEvent(oDiv, "click", click1);
}
function click1() {
    alert("click1");
}
function click2() {
    alert("click2");
}
</script>
<style type="text/css">
#oDiv {
 width: 80px;
 height: 80px;
 border: 1px solid red;
 padding: 10px;
    color: red;
}
</style>
</head>
<body>
    <div id="oDiv">这是一个测试DIV,点我</div>
</body>
</html>


layerX/layerY 与 offsetX/offsetY 的差别请参考:http://my.oschina.net/banbo/blog/342356








你可能感兴趣的:(Javascript 跨浏览器绑定事件)