Event Handling in IE
I have the code included below that allows me to do something the first time a user mouses over an element and then remove the event.
It plays nice in the W3C event model browsers but keeps throwing an error in IE6-8. I got the code from another question and believed it would handle IE. Anyone see what I'm doing wrong?
<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>
Update: I just tested in the latest Chrome, Opera and Firefox with no issues but Safari does nothing when I mouse over and IE is throwing the errors onload
as mentioned.
event = addEvent(
addEvent
doesn't return anything; you're assigning the undefined
lack-of-return-value to a global variable event
(since you've not said var event
in the function). This will cause an exception in IE, which uses the global event
as a special object to pass event details to handlers, and consequently doesn't allow you to assign something else to it.
if (typeof obj.addEventListener != undefined)
typeof
always returns a string, which will never test equal to the undefined
unvalue, so IE will always take the non-IE fork and fail. You meant if (typeof obj.addEventListener !== 'undefined')
, with a string.
obj.detachEvent("on" + type, obj[type + fn]);
Since you've not written a property with the name of type
and fn
stuck together in the addEvent
function, this will fail to retrieve anything.
As Crescent says, it looks like you're using Resig's removeEvent
function, paired with a different addEvent
that doesn't match. If you use his removeEvent
you will need to use his addEvent
to go with it.
However, I wouldn't use these functions anyway: they're pretty dodgy. I know it was 2005 when this ill-conceived code ‘won’ quirksmode's addEvent contest, but even then we should have known a lot better. The problem is it creates a string from the event name and the textual serialisation of the function code (type+fn
), and uses it as a key to store a callback. This key will look something like 'mouseoverfunction changeText() {...code...}'
.
But relying on function serialisation is a terrible idea. The format is not standardised by ECMAScript (“An implementation-dependent representation of the function is returned.”); there are many browser quirks; and not least, two different functions can easily return the same string for browsers' current serialisation methods:
var f1= function() {};
var f2= function() {};
f1
and f2
will have the same string serialisation, but they're not the same object. If you did an addEvent
for f1
on IE, and then another one for f2
, the second property would use the same serialised string and overwrite the first property. Then calling removeEvent
for f1
would retrieve the function for f2
, try to detachEvent
it and fail because it's not the same function. This example may look contrived, but it's actually extremely easy to do accidentally when you're using generic closures, as modern JavaScript does more and more these days. For this reason I'd recommend avoiding Resig's addEvent
in all circumstances.
(jQuery users: don't worry, for all its problems jQuery does not fall into the trap of using this code.)
This hack is there to preserve the this
value when your changeText
function is called back by IE, whose attachEvent
doesn't set this
. But you don't even use the this
value, so you could have got away with a much simpler version, such as the original addEvent it was created to replace.
At least the shortcomings of that addEvent are well-known and show up immediately when you test on IE using this
, instead of only going wrong in a particular case that, when it affects you, is likely to be extremely confusing and hard to debug.
Then again, you aren't currently using even multiple listeners for the same event, so you could easily get away with a old-fashioned DOM Level 0 event handler, something like:
window.onload= function() {
var changed= false;
document.getElementById('contactButton').onmouseover= function() {
if (!changed) {
alert('changing text');
}
changed= true;
};
};
Go for Dean's addEvent, if you want a flexible solution:
// 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;
};
精彩评论