2

Trying to build some drag'n'drop functionality and got a weird bug in Firefox. I have the whole document's body as a drop zone and want to change h1 value when some object is being dragged over the page.

Seems like no problem at all: http://jsfiddle.net/j7u6hLyj/

It works as it should everywhere, except Firefox. When I'm dragging an object through the page - it's no problem, except the situation when the object is being dragged over the text, if you'd try the example and drag a file over the text you would notice that ondragenter event is being fired non-stop while the object is floating over the text. It happens only in Firefox.

Danny
  • 444
  • 4
  • 19
  • 2
    `ondragcenter` looks like a typo. Perhaps you mean `ondragenter`? – Rūdolfs Vikmanis Nov 20 '15 at 00:18
  • I'm able to resolve the issue by getting rid of all your HTML except for the . So, while it's possible browsers should be able to handle that, maybe you could work around it by somehow displaying the text in a different way that won't interfere with mouse event bubbling. – Katana314 Nov 20 '15 at 00:21

2 Answers2

2

It seems as though the changing of the innerHTML is resetting the state of whether or not the event has already been raised, thereby causing it to get stuck in a loop. This may be a bug in Firefox. Adding a flag to only change the text when it hasn't previously been changed prevents it from getting stuck in this loop. You could then handle the dragleave event to clean up this flag if needed.

var drop = document.getElementById('drop'),
text = document.getElementById('promo-text');
var textChanged = false;

drop.ondragenter = function () {
    console.log('dragged!');
    if (!textChanged) {
        text.innerHTML = 'Drop it here!';
        textChanged = true;
    }
    return false;
};

This code running on jsfiddle: http://jsfiddle.net/17aq9dhs/

Adam Shaver
  • 133
  • 1
  • 6
1

The root cause of the problem is that your code is setting up a feedback loop. In Firefox at least.

When any ondragenter event propagates to your body element you code modifies the displayed text by setting the element's innerHTML property. The browser updates the element in the DOM, and in the case of Firefox sees that you're dragging something over the updated element and retriggers the ondragenter event (which then propagates back up to your body element and restarts the whole process).

To fix that, all you have to do is pull one link out of the chain to break the feedback loop. For instance, by tracking whether or not you've already changed the text for a given 'drag' event (essentially as Adam Shaver suggests), or by checking to see if the text already says "Drop it Here!" before assigning to the innerHTML property, etc..

Though be careful with that approach if you want to have your handler work across multiple 'drag' events (i.e. have the text toggle back and forth between "Drop file somewhere here." and "Drop it here!" whenever the user starts a 'drag' operation and then cancels/ends it without dropping the file). You'll have to run your accounting correctly on both ends if you want that to work.

Another option to break the feedback loop is to cancel the events triggered by the text element so that they don't propagate up to body in the first place. For instance, like:

<h1 class="cover-heading" id="promo-text" ondragenter="cancelEvent(event);">Drop file somewhere here.</h1>

[...]

function cancelEvent(evt) {
    if (! evt) {
        evt = window.event;
    }

    if (evt) {
        console.log("Canceling event");
        evt.cancelBubble = true;
        if (evt.stopImmediatePropagation) {
            evt.stopImmediatePropagation();
        }
    }

    return false;
}

http://jsfiddle.net/j7u6hLyj/2/

That may work better based upon your current setup, as it's less manual bookkeeping to worry about.

Other approaches might also work, such as inspecting the event's target (or srcElement on IE). Essentially anything that breaks the feedback loop will do.

aroth
  • 54,026
  • 20
  • 135
  • 176
  • Thank you Sir for an exhaustive answer! – Danny Nov 20 '15 at 10:47
  • Hi Adam found your awesome screen capture code from 2011. Since this was from 2011 is this still the best way to do screen capture today? We specifically need to capture the contents of a UIView, not the whole screen. Thanks for your time! Question: http://stackoverflow.com/questions/34956713/simplified-screen-capture-record-video-of-only-what-appears-within-the-layers-o – Crashalot Jan 22 '16 at 22:17
  • @Crashalot - I haven't tried running the code recently, but given its age I'd tend to think there may be alternative/better methods to capture view contents by now. Apple frequently updates the iOS SDK, and often introduces at least a few breaking changes each time they do. Simplest thing is probably to try it and see if it still compiles and runs. – aroth Jan 23 '16 at 00:19
  • @aroth Thanks Adam. Do you happen to have a Swift version? Are you available for consulting? We don't need code, just advice on issues like this since we're new to AVFoundation. Thanks again! – Crashalot Jan 23 '16 at 00:32