#7278 closed bug (cantfix)
Error preventing delayed special events in IE
Reported by: | scottgonzalez | Owned by: | |
---|---|---|---|
Priority: | high | Milestone: | |
Component: | event | Version: | 1.4.3 |
Keywords: | special events | Cc: | |
Blocked by: | Blocking: |
Description (last modified by )
We ran into a problem using the hoverintent special event with accordions in jQuery UI. I've narrowed down the problem to calling event.preventDefault() when the special event is triggered through a timeout from a native event. For some reason IE won't let you set event.returnValue inside event.preventDefault().
The only solution I've been able to find is wrapping the e.returnValue = false
in a try/catch inside jQuery.Event.prototype.preventDefault().
full demo: http://jqueryui.com/demos/accordion/hoverintent.html
reduced test case: http://jsfiddle.net/GSD84/1/
Change History (11)
comment:1 Changed 12 years ago by
Description: | modified (diff) |
---|
comment:2 Changed 12 years ago by
Component: | unfiled → event |
---|---|
Description: | modified (diff) |
comment:3 Changed 12 years ago by
Keywords: | special events added |
---|---|
Priority: | undecided → high |
Status: | new → open |
comment:4 Changed 12 years ago by
comment:5 Changed 12 years ago by
Resolution: | → cantfix |
---|---|
Status: | open → closed |
I think the problem is that in IE the global event
object only exists during the time the event is being delivered. That is some special IE host object and not a standard Javascript object that can be held in a closure. When the code is called in a timeout the event delivery is done so event
object has dissolved and is no longer accessible.
See the comments here: http://msdn.microsoft.com/en-us/library/ms535863%28v=vs.85%29.aspx
As a workaround you could try newEvent = document.createEventObject(event)
or perhaps newEvent = $.extend({}, event)
if you need an honest JS object. In any case I think this is a limitation of IE that we can't circumvent so you'll need to fake something up.
comment:6 Changed 11 years ago by
Could we potentially set a flag that causes preventDefault()
and other methods that we normalize to not bubble to the original event if the're called asynchronously?
comment:7 Changed 11 years ago by
Since we're feature-detecting the presence of the preventDefault method, it should be okay to do this in your handle hook:
http://jsfiddle.net/GSD84/11/
You could copy over any properties you need of course. To prevent clobbering subsequent attached handlers you'll need to restore event.type and event.originalEvent before returning though, which means you also need to make a copy of the event that setTimeout can use when it fires.
There's no way to handle this generically in core without knowing you intend to hold onto the event object past the normal delivery of the event. Are there are lots of cases like this, or is hoverIntent the main usage?
comment:8 Changed 11 years ago by
I'm sure there are other use cases. Off the top of my head, the only other event I can think of is taphold. jQuery Mobile implemented taphold, but they don't pass any of the original event information along. If they did, it would have to come from an event that is already gone, such as mousedown or touchstart. I'll try to put together a patch with the generic solution I was thinking of, though it may not be worth landing even if I can implement it.
comment:9 Changed 11 years ago by
Ok, so we can't implement the fix I was thinking of, but wrapping event.returnValue = false;
in a try/catch fixes the problem. This seems really painful for developers to work around inside their own special events, especially when supporting multiple versions of jQuery.
comment:10 Changed 11 years ago by
It's going to crash on any direct/indirect use of the now-defunct originalEvent inside the user's handler, not just that one. Technically the code isn't doing what it intended on any platform, since a delayed .preventDefault()
or .stopPropagation()
won't have any effect. By making the handler async it's become impossible to manipulate the event state since the event is technically over. It's just a question of whether that happens silently or with an error.
If you don't need originalEvent in the timeout can you try setting it to an empty Javascript object instead of leaving the global IE event object in the closure? That seemed to work in the fiddle I posted. Something like that should apply to all versions of jQuery so you have backcompat, which the try/catch won't do.
I'm not sure if this will be at all helpful, but in my own experience with this issue, it's not just
e.returnValue
but all members of the event object that are inaccessible, includinge.cancelBubble
fromevent.stopPropagation()
.In my case, I'm using the jQuery Tools tooltip plugin with a delay. When handling a custom event that fires just before the tooltip is shown, I do a test that would allow me to prevent the appearance of the tooltip by returning
false
from the handler. When I do that, and consequentlyjQuery.handle()
callsevent.preventDefault()
andevent.stopPropagation()
, both of them have trouble accessing the original event object.I used the IE script debugger, and at that point in time, every property of the event object is marked as
Member not found
.