Bug Tracker

Opened 7 years ago

Closed 6 years ago

Last modified 5 years ago

#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 scottgonzalez)

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 7 years ago by scottgonzalez

Description: modified (diff)

comment:2 Changed 7 years ago by scottgonzalez

Component: unfiledevent
Description: modified (diff)

comment:3 Changed 7 years ago by SlexAxton

Keywords: special events added
Priority: undecidedhigh
Status: newopen

comment:4 Changed 6 years ago by ravasthi@…

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, including e.cancelBubble from event.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 consequently jQuery.handle() calls event.preventDefault() and event.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.

comment:5 Changed 6 years ago by dmethvin

Resolution: cantfix
Status: openclosed

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 5 years ago by scottgonzalez

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 5 years ago by dmethvin

Since we're feature-detecting the presence of the preventDefault method, it should be okay to do this in your handle hook:


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 5 years ago by scottgonzalez

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 5 years ago by scottgonzalez

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 5 years ago by dmethvin

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.

comment:11 Changed 5 years ago by scottgonzalez

I suppose that's fine. You win this round :-)

Note: See TracTickets for help on using tickets.