2010-07-06 64 views
2

我有下面的代码,它允许我在用户第一次将元素放在元素上然后移除事件时执行某些操作。IE中的事件处理

它在W3C事件模型浏览器中玩的很好,但在IE6-8中一直抛出一个错误。我从另一个问题得到了代码,并相信它会处理IE。任何人看到我做错了什么?

<script type="text/javascript"> 
    function setMouseEvent() { 
     //Tel: 01 8279 400 
     event = addEvent(document.getElementById('contactButton'), 'mouseover', changeText); 
    } 

    function changeText() { 
     alert("worked!"); 
     removeEvent(document.getElementById('contactButton'), 'mouseover', changeText); 
    } 

    function addEvent(obj, type, fn) { 
     if (typeof obj.addEventListener != undefined) { 
      obj.addEventListener(type, fn, false); 
     } 
     else if (typeof obj.attachEvent != undefined) { 
      obj.attachEvent("on" + type, fn); 
     } 
    } 

    function removeEvent(obj, type, fn) { 
     if (typeof obj.addEventListener != undefined) { 
      obj.removeEventListener(type, fn, false); 
     } 
     else if (typeof obj.attachEvent != undefined) { 
      obj.detachEvent("on" + type, obj[type + fn]); 
      obj[type + fn] = null; 
      obj["e" + type + fn] = null; 
     } 
    } 

    window.onload = setMouseEvent; 
</script> 

更新:我只是在最新的Chrome,Opera和FF没有问题的测试,但Safari浏览器也没有,当我鼠标悬停和IE浏览器的onload抛出错误提到。

+1

呸。你从哪里得到'removeEvent'?看起来类似于John Resig的实现(http://ejohn.org/blog/flexible-javascript-events/),但与'addEvent'不对称。 – 2010-07-06 10:53:11

+0

也许我只是一个傻瓜,但如果这是我的页面,我会用一个简单的标志变量处理一次性事件处理程序,我会在第一次调用后翻转它。如果我真的想做很多事件争吵,我会让一些经过良好测试的框架处理丑陋的细节。 – Pointy 2010-07-06 12:02:17

+0

我很接近只是再次抓住jQuery,但这次想挖一点点。一直想继续学习。 – 2010-07-06 21:30:49

回答

5
event = addEvent(

addEvent不返回任何东西;您将undefined缺少返回值分配给全局变量event(因为您在函数中没有说var event)。这会在IE中引发异常,它使用全局event作为特殊对象将事件详细信息传递给处理程序,因此不允许您为其分配其他内容。

if (typeof obj.addEventListener != undefined) 

typeof总是返回一个字符串,它永远不会测试等于undefined unvalue,所以IE将始终以非IE叉和失败。你的意思是if (typeof obj.addEventListener !== 'undefined'),带有一个字符串。

obj.detachEvent("on" + type, obj[type + fn]); 

既然你不写的属性与typefnaddEvent功能粘在一起的名字,这将无法获取任何东西。

正如Crescent所说,它看起来像是使用Resig的removeEvent函数,与不匹配的addEvent不同。如果您使用他的removeEvent,则需要使用他的addEvent才行。

但是,我不会使用这些函数:它们非常狡猾。我知道这是2005年,当这个错误的代码'赢得'quirksmode的addEvent比赛,但即使如此,我们应该知道更多。问题是它会根据事件名称和功能代码的文本序列号(type+fn)创建一个字符串,并将其用作存储回调的键。这个键看起来像'mouseoverfunction changeText() {...code...}'

但依靠函数序列化是一个可怕的想法。格式不被ECMAScript标准化(“函数的实现相关表示被返回”);有many browser quirks;而并非最不重要的,两种不同的功能可以很容易地返回相同的字符串 - 浏览器目前的序列化方法:

var f1= function() {}; 
var f2= function() {}; 

f1f2将具有相同的字符串序列化,但它们不是同一个对象。如果你在IE上为f1执行addEvent,然后在f2上执行另一个,则第二个属性将使用相同的序列化字符串并覆盖第一个属性。然后调用removeEventf1将检索功能f2,尝试detachEvent它并失败,因为它不是相同的功能。这个例子可能看起来很有意思,但是当你使用通用闭包的时候,它实际上是非常容易的,因为现代JavaScript越来越多。由于这个原因,我建议在所有情况下避免Resig的addEvent

(jQuery的用户:不用担心,它的所有问题jQuery的不属于使用此代码的陷阱)

这个技巧是有保存价值this当你changeText函数被调用回由IE,其attachEvent不设置this。但你甚至不使用this值,所以你可以用一个更简单的版本,比如它创建的original addEvent来代替。

在至少缺点是的addEvent是众所周知的,并立即显示出来,当你在IE测试使用this,而不是只在特殊情况下,当它影响到你,很可能是极其脚麻的混乱和难以调试。

话又说回来,你现在还没有使用,即使是同一事件的多个监听器,让您可以轻松地摆脱了老式的DOM 0级事件处理程序,是这样的:

window.onload= function() { 
    var changed= false; 
    document.getElementById('contactButton').onmouseover= function() { 
     if (!changed) { 
      alert('changing text'); 
     } 
     changed= true; 
    }; 
}; 
+0

我认为这是第一个解决Resig的addEvent条目的问题。在这个答案的评论中有一个简短的交流:http://stackoverflow.com/questions/1466875/ies-understanding-of-this/1471006#1471006其中kangax启发我依靠函数反编译/序列化的失败标识一个函数对象。好写。 A ++++++++ – 2010-07-06 23:20:37

+0

+1秒。 – galambalazs 2010-07-11 00:21:52

1

转到为Dean's addEvent,如果你想有一个灵活的解决方案:

// written by Dean Edwards, 2005 
// with input from Tino Zijdel, Matthias Miller, Diego Perini 
// http://dean.edwards.name/weblog/2005/10/add-event/ 

function addEvent(element, type, handler) { 
    if (element.addEventListener) { 
     element.addEventListener(type, handler, false); 
    } else { 
     // assign each event handler a unique ID 
     if (!handler.$$guid) handler.$$guid = addEvent.guid++; 
     // create a hash table of event types for the element 
     if (!element.events) element.events = {}; 
     // create a hash table of event handlers for each element/event pair 
     var handlers = element.events[type]; 
     if (!handlers) { 
      handlers = element.events[type] = {}; 
      // store the existing event handler (if there is one) 
      if (element["on" + type]) { 
       handlers[0] = element["on" + type]; 
      } 
     } 
     // store the event handler in the hash table 
     handlers[handler.$$guid] = handler; 
     // assign a global event handler to do all the work 
     element["on" + type] = handleEvent; 
    } 
}; 
// a counter used to create unique IDs 
addEvent.guid = 1; 

function removeEvent(element, type, handler) { 
    if (element.removeEventListener) { 
     element.removeEventListener(type, handler, false); 
    } else { 
     // delete the event handler from the hash table 
     if (element.events && element.events[type]) { 
      delete element.events[type][handler.$$guid]; 
     } 
    } 
}; 

function handleEvent(event) { 
    var returnValue = true; 
    // grab the event object (IE uses a global event object) 
    event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); 
    // get a reference to the hash table of event handlers 
    var handlers = this.events[event.type]; 
    // execute each event handler 
    for (var i in handlers) { 
     this.$$handleEvent = handlers[i]; 
     if (this.$$handleEvent(event) === false) { 
      returnValue = false; 
     } 
    } 
    return returnValue; 
}; 

function fixEvent(event) { 
    // add W3C standard event methods 
    event.preventDefault = fixEvent.preventDefault; 
    event.stopPropagation = fixEvent.stopPropagation; 
    return event; 
}; 
fixEvent.preventDefault = function() { 
    this.returnValue = false; 
}; 
fixEvent.stopPropagation = function() { 
    this.cancelBubble = true; 
};