1

I am creating a calendar using the html5 drag and drop api.

I am using dragEnter and dragLeave to style the receiving element and determine droppablity.

The receiving droppable element has children and dragLeave is being called when dragging over a child element. This problem is know and described here.

My attempted solution was to add dragLeave and dragEnter events on the children and pass the logic back to the parentView.

Here is the code. http://jsbin.com/iHaNuPo/17

The problem is `dragEnter' on the child is called before 'dragLeave' on the parent.

This is the order of events.

#in from outside parent to over child
1. parent-enter 
2. child-enter 
3. parent-leave
#out from over child to outside parent
1. parent-enter
2. child-leave
3. parent-leave

Is this a bug?

Anyone know a Ember'ish way to prevent the dragLeave event of the parent from firing when hovering over a child?

Or a way to fix the order in which events dragLeave, dragEnter fire?

Should I ditch html5 native drag and drop and use jquery ui draggable/droppable?

EDIT

Here is a working example as per Woody's answer.

http://jsbin.com/OjAGabUj/22

Community
  • 1
  • 1
Aaron Renoir
  • 4,283
  • 1
  • 39
  • 61
  • Should you ditch native d-n-d? Probably, it will ensure your compatibility if you use a library that has shims. Otherwise, it will defer to native anyway. – Steve H. Jan 08 '14 at 03:10

2 Answers2

3

Hopefully this can help you although it's not Ember, maybe you can make use of it. I've had a fair bit of experience of HTML5 drag and drop and it's a bit irksome, but if you want cool stuff like cross window drag and drop it's worth the effort I think.

var ignoreNextLeave = false;

$("#outerDragTarget").on("dragenter", function(ev) {
   // not interested in dragleaves from my children...
   if (ev.target !== this) {
      ignoreNextLeave = true;
   }

   $(this).addClass("dragover");

}).on("dragleave", function(ev) {

   if (ignoreNextLeave) {
      ignoreNextLeave = false;
      return;
   }

   $(this).removeClass("dragover");
});

$("#innerDragTarget").on("dragleave", function(ev) {
  ev.stopPropagation();
});

You can run it here

The trick is to cancel propagation of dragleaves from the inner element and note that when drag enter fires on the parent and the target is not the parent, we're about to get a drag leave we're not interested in. Not ideal but it could be factored into something you can hopefully use.

Woody
  • 7,578
  • 2
  • 21
  • 25
  • Awesome that works! I added an example implementing your method. – Aaron Renoir Jan 08 '14 at 03:27
  • Cool ! HTML5 drag and drop has a few rough edges and patchy support on IE. From IE10 things start to get a bit better although there are still problems but most of these can be worked around. I have been spending a fair bit of time working with this stuff at my job over the past year so check out my other questions and answers - they more or less contain all the useful stuff I learned :) – Woody Jan 08 '14 at 10:23
  • So in short i would use HTML5 drag and drop if you can, as far as I'm aware none of the drag and drop frameworks available use native drag and drop. Native drag and drop is the only game in town if you want cross window drag drop or drag into another application. – Woody Jan 08 '14 at 10:34
  • Yeah it is a bit rough. I am only working with webkit so I am going to stick with html5. Do you think the order of events (outerEnter, innerEnter, OuterLeave) is a bug in chrome or part of the dnd spec? Thanks! – Aaron Renoir Jan 08 '14 at 19:51
  • It is the intended behaviour from the specs. Even though it is really counterintuitive and often problematic :/ – Kamafeather Jan 20 '16 at 23:15
1

I think Woody's solution is correct, but if you want to save a few lines of code and avoiding having to fiddle with child elements directly, you could also use a reference counting solution:

$(".drop-target").on("dragenter dragleave", function (e)
{
  var dragHoverCount = Number($(this).data("dragHoverCount")) || 0;
  (e.type === "dragenter") ? dragHoverCount++ : dragHoverCount--;
  $(this).data("dragHoverCount", dragHoverCount);

  $(this).toggleClass("drag-hover", dragHoverCount > 0);

  e.preventDefault();
});
s0ma7
  • 26
  • 5