Bug Tracker

Modify

Ticket #7278 (closed bug: cantfix)

Opened 3 years ago

Last modified 2 years ago

Error preventing delayed special events in IE

Reported by: scott.gonzalez Owned by:
Priority: high Milestone:
Component: event Version: 1.4.3
Keywords: special events Cc:
Blocking: Blocked by:

Description (last modified by scott.gonzalez) (diff)

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

comment:1 Changed 3 years ago by scott.gonzalez

  • Description modified (diff)

comment:2 Changed 3 years ago by scott.gonzalez

  • Component changed from unfiled to event
  • Description modified (diff)

comment:3 Changed 3 years ago by SlexAxton

  • Keywords special events added
  • Priority changed from undecided to high
  • Status changed from new to open

comment:4 Changed 3 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 3 years ago by dmethvin

  • Status changed from open to closed
  • Resolution set to cantfix

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 2 years ago by scott.gonzalez

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 2 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:

 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 2 years ago by scott.gonzalez

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 2 years ago by scott.gonzalez

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 2 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 2 years ago by scott.gonzalez

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

Please follow the  bug reporting guidlines and use  jsFiddle when providing test cases and demonstrations instead of pasting the code in the ticket.

View

Add a comment

Modify Ticket

Action
as closed
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.