I a building a small filemanager with dropzone.js and it works fine with firefox but with ie, chrome and safari there is a lot of flickering of the border when dragging the mouse oer child elements. Is there a way to avoid triggering the dragleave event when themouse enters child elements?
claudio
Nope that's a thing, but you can code around it. See the discussion here: http://stackoverflow.com/questions/14194324/firefox-firing-dragleave-when-dragging-over-text/20596358#20596358
I found "dragout" to be very useful.
I've found drag enter to have the same issue (it does not fire for children). Its an issue for small parents with larger children with fast mouse movements. One day I may write its counterpart. Dragon?
I have solved the issue with Dragster:
https://github.com/bensmithett/dragster
It is small and it works great, may be it can be included in dropzone.
Here what I did:
redefined dragenter and dragleave:
dragleave: function(ev) {return true},
dragenter: function(ev) {return true},
create dragster class:
var ds = new Dragster( div_element );
Add dragster listener
document.addEventListener( "dragster:enter", function (e) {
e.target.classList.add( "dz-drag-hover" );
}, false );
document.addEventListener( "dragster:leave", function (e) {
e.target.classList.remove( "dz-drag-hover" );
}, false );
That's it.
Will think about how this could be improved
If it helps I fix the issue the following way:
initialize: function () {
this.dragEnteredEls = [];
},
dragenter: function (e) {
this.dragEnteredEls.push(e.target);
if (this.el === e.target) {
// render hover div
}
},
dragleave: function (e) {
this.dragEnteredEls = _.without(this.dragEnteredEls, e.target);
if (this.dragEnteredEls.length === 0) {
// cleanup, dragging is finished
}
}
I was able to work around this by using a transparent overlay.
Basically a div that is 100% height and width of the drop zone that appears when .dz-drag-hover is present on the drop zone. If you add a z-index to it, it will cover up all of the other child elements. I'm really not sure why the overlay element wasn't causing flickering itself, but it works and is CSS only, so :+1:
@neiltron Just tried your idea and whilst it works when the hover is not in place the child elements are not clickable. EG: links. Text is selectable though. I need to try something else now.
Here is some sample code that I'm using to fix this. I've packaged it up so that it should be able to just drop into your projects. :sparkles:
function setupDragon(uploader) {
/* A little closure for handling proper
drag and drop hover behavior */
var dragon = (function (elm) {
var dragCounter = 0;
return {
enter: function (event) {
event.preventDefault();
dragCounter++;
elm.classList.add('dz-drag-hover')
},
leave: function (event) {
dragCounter--;
if (dragCounter === 0) {
elm.classList.remove('dz-drag-hover')
}
}
}
})(uploader.element);
uploader.on('dragenter', dragon.enter);
uploader.on('dragleave', dragon.leave);
}
// You can set it up via the custom intialization
new Dropzone('div#dropzone', {
url: "/upload",
/* overwrite the default behavior */
dragenter: function () {},
dragleave: function () {},
/* setup the new behavior */
init: function () { setupDragon(this) }
});
// Or using the options like this
Dropzone.options.dropzone = {
/* overwrite the default behavior */
dragenter: function () {},
dragleave: function () {},
/* setup the new behavior. 'this' is the uploader */
init: function () { setupDragon(this) }
}
Thanks to @mkasson for the name idea. :+1:
@jhubert Works like a dream! Thanks so much for this.
@jhubert Reference counting will only get you so far with this problem. Depending on browser (firefox) and sophistication of the dropzone (nested elements) it will rapidly break down.
I've personally used something very similar to what @malomalo describes, though he's not particularly clear with his description.
Basically the idea is that you have an array, and you add e.target for every single dragEnter event you receive. You similarly remove every single e.target you receive within dragLeave.
When the array is of length 0 you remove the hover effect within dragLeave. You may unconditionally add the hover effect within dragEnter.
You'll note that removing a specific element from an array can be a bit of a pain here, though it's rather easy with underscore or jQuery. The other poster roughly covers the underscore approach, here is how it'd look with jQuery:
```
init: function() {
this.dragEnteredEls = $();
},
dragenter: function(e) {
this.dragEnteredEls.add(e.target);
$(this.element).addClass('dz-drag-hover');
},
dragleave: function(e) {
this.dragEnteredEls = $(this.dragEnteredEls).not(e.target);
if (this.dragEnteredEls.length === 0) {
$(this.element).removeClass('dz-drag-hover');
}
},
Not exactly hard, and covers all the edge cases that I'm aware of. I've had something in production using something similar to the above for about 6 months without issue now.
Thanks @kamelkev !
@Ydalb It's worth noting my original post here had a minor error. You want "$()" instead of "new Array" for dragEnteredEls, otherwise there are some cross browser issues. You then want to use the jquery specific array modifiers (because dragEnteredEls is now a jquery object), after which everything works as expected.
Apologies. I've fixed my example, you might want to fix up your merge and double check it.
I’ve been stuck with drag events the entire day, and just realized that my use case is slightly different: I want to show dropzones when a file dragenters the viewport, and hide them again when it dragleaves. However, as dropzone.js stops event propagation in its internal dragenter event handler, document will never get the event, with the result that moving the file onto the dropzone is counted as a dragleave instead.
I’ve modified the source code so that dragenter events bubbles from the dropzone, but I don’t know what side effects this change might have. Any insight here? Or proposals for better workarounds?
Looking at this more closely, it seems more and more like there’s some confusion between stopping _event propagation_ and preventing _default browser behaviors_, since both are called from a function named noPropagation, weirdly. They don’t do the same thing, at all.
I’ve commented out most instances of calls to the noPropagation function, and replaced with e.preventDefault() where needed, in order to allow for other parts of the code to listen in on what’s going on – as I would expect to be able to do out of the box.
It would be nice if someone with deeper insight into and knowledge of the library, and the decisions leading up to it being the way it is, would review this part of the code and see if this could be updated to allow for event propagation.
For my case @kamelkev solution worked perfectly AND he was exactly right about @jhubert solution causing issues in Firefox (I got bitten).
@Haraldson it seems like your proposal would probably make sense as a separate issue and Pull Request.
The solutions didn't work for me. Used a counter instead
Edit: Ended up using @kamelkev fix instead
@sorenfu Such a solution cannot and will not work with Firefox. You likely won’t notice problems elsewhere, but you will note obvious tabulation issues if you test in Firefox.
The underlying reason relates to how Firefox decides to treat the enter event. Entering a certain elements may allow multiple enter events to fire, however there won’t be an analogous set of exit events to match. The net result is that your counter can always increase, but won’t necessarily decrease as expected.
The solution I have posted is tried and true. My team spent an inordinate amount of time testing the technique, which is now used by multiple other organizations as well. You should retry it.
@kamelkev you are right. The reason why I still had was the flickering, was because I had a :before element on .dz-drag-hover with a text. I moved the text into a child tag and that did it. js ❤️
Just figured out a sneaksy way of doing this.
If you give yourself something like this:
.dz-drag-hover {
position: relative;
}
.dz-drag-hover:after {
display: block;
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
background-color: transparent;
z-index: 1000;
}
.dz-drag-hover .your-visual-dropzone-upload-message {
/* ... */
}
And you style the element that you want shown to the user so the z-index isn't above that 1000, it doesn't flicker.
The idea is that the :after is overlaid on top as a transparent layer so the drag event is kept on that, but your upload visual is still shown because of the presence of the .dz-drag-hover class on a parent container.
@kamelkev your fix didn't work for us initially. However thanks for the tip, good job - I did a simple change and everything works now in any browser:
dragenter: function(e) {
this.dragEnteredEls = this.dragEnteredEls.add(e.target);
$(this.element).addClass('dz-drag-hover');
},
That's interesting. Are we saying that .add does not add the passed item to the collection, but instead returns a whole new collection? That would be incredibly interesting if so...
Maybe someone else can comment regarding whether they had similar issues or not?
@kamelkev I just had the same issue as @serbiant did and used his fix.
Same here @kamelkev. Seems that .add doesn't change the original collection:
From https://api.jquery.com/add/
The following will not save the added elements, because the .add() method creates a new set and leaves the original set in pdiv unchanged:
var pdiv = $( "p" );
pdiv.add( "div" ); // WRONG, pdiv will not change
@micaww Good to know.
Hi, I'm having this issue, is there a fix now ?
I had the same problem, here's a simple fix that worked for me.
(written in Typescript, but the Javascript/Jquery equivalent should work fine)
1- on the 'dragover' event, call a functin that adds a class to the dropzone container (e.g., .no-pointer-events)
in your css file
.no-pointer-events * {
pointer-events: none;
}
,
The function:
public onFileDrag() {
this.isFileDragging = true;
if (this.draggingTimeout) {
clearTimeout(this.draggingTimeout);
}
this.draggingTimeout = setTimeout(() => {
this.isFileDragging = false;
}, 500);
}
Basically, this is to keep the class on the dropzone to disable all children pointer events as long as there is a file hovering, when there is none, the children return to normal after half a second
Most helpful comment
Here is some sample code that I'm using to fix this. I've packaged it up so that it should be able to just drop into your projects. :sparkles:
Thanks to @mkasson for the name idea. :+1: