Skip to content
Advertisement

Event listener conflict between jQuery and OpenLayers

The application is a web map (OpenLayers 2) on which one can open dialog boxes by clicking certain features. Dialog boxes are handled with jQuery-ui.

The Bug: On resizing a dialog, if the user drags the cursor too quickly, the cursor outpaces the size updating of the dialog. Normally this would require pretty jerky movements, but due to just how packed this application has gotten I guess it’s a little bit slower.

But there’s some event-handling conflict between jQuery and OpenLayers. When the cursor outpaces the div anywhere else it’s fine, but if it’s over the map, the resize stops (as well if you mouse-up, the stop drag state isn’t registered so if you return to the dialog, it resizes with mouse movement).

This means to expand the dialog, you have to move the cursor intentionally smoothly and not too fast otherwise it breaks, which is obviously too problematic for the user experience.

Set up a very simple jsfiddle (http://jsfiddle.net/a6uu5vav/) simply try to drag or resize the dialog quickly. May vary by computer/browser, but working (that is, not working) for me in Chrome.

The Reason: jQuery-ui seems to attach the event listeners related to drag/resize to document so the functionality should work regardless of mouse outpacing div. However OpenLayers also puts a ton of event listeners on everything it creates. It uses event.stopPropagation() so that only the listeners on the target element are notified however.

So, when the cursor is over the OpenLayers map, it fires the target’s event listeners, but the event does not propagate to the resize listeners. While commenting out stopPropagation() technically solves it, because of how nested OpenLayers is and how it gives event listeners to everything, this makes it pretty slow just hovering over the map.

I tried making the dialog modal to test that it is indeed conflicting event listeners. Modal fixes the resize issue, but we don’t want it to be modal. Potentially though a “hacky” solution is to have an invisible modal and appears/disappears with the mousedown/up events that start/end the drag.

My Fix Attempt: I went into jQuery-ui (in the ui.mouse definition) to modify the event listeners to fire on capture (instead of bubble), which since they are added to the document will then be guaranteed to fire. Since jQuery.bind() doesn’t support that, I switched to old-school addEventListener(), for consistency I also changed any related unbind() to removeEventListener().

Now here’s the thing, if I only did that, but still set the onCapture param for addEventListener() to false, it works exactly how it did before — working as usual with the resize bug. If I change the onCapture param to true for the mousemove listener (doesn’t matter if I set mouseup to true/false), the resize problem is fixed, technically, but now other stuff is being wonky.

It drags/resizes fine, but on mouseup, the event listeners don’t seem to be removed (thus moving mouse still drags or resizes the dialog), also div shifts for no reason. _mouseUp() is being called and the event listeners removed at least in those lines, I know it’s not a callback reference issue, I’ve tested for that. I think they are getting removed properly but somehow readded. Seems there’s something in jQuery firing on mouse-up that has to go first and somehow rearranging order, even just that bit completely throws it off.

This might be a losing game (i.e. total time sink relative to seriousness of bug) to try and “patch” jQuery-ui for this, but wondering if there may be some other ideas on how to try and solve this.

Answer

Ugh, I feel silly. My fix idea does work, but if you set the addEventListener()‘s onCapture param to true, you have to set the onCapture param in removeEventListener() to true as well.

As for the full fix for the original problem, you have to edit the bind() and unbind() in jQuery-ui for mousemove and mouseup to addEventListener() and removeEventListener() with onCapture=true for both, for these definitions:

$.ui.mouse._mouseDestroy(), $.ui.mouse._mouseDown(), and $.ui.mouse._mouseUp()

So far it appears to be working well for me.

Advertisement