jQuery droppable – 在拖动过程中接收事件(不仅仅是在初始拖动时)

我使用jQuery droppable (与jQuery draggable结合使用)允许用户通过从列表中拖动项目并将其放在表格上来向HTML表格添加行。

这很好用,但是目前的逻辑是,当用户在表行上拖放时,新行将被添加它们所放置的行的下方

如果新行的添加位置基于用户是否放入现有行的上半部分或下半部分,那会更好。

这很容易在drop事件中计算,但是我需要在用户拖动时提供UI反馈(我将通过两个CSS类droppable-abovedroppable-below来实现)。

这似乎不可能,因为over事件只发射一次; 当用户最初拖动可投放元素时。

当用户在可放置元素上时,是否可以为每次鼠标移动触发over事件?

如果是这样,那么我就能做到这一点:

 $("tr.droppable").droppable({ over: function(event, ui) { if (/* mouse is in top half of row */) { $(this).addClass("droppable-above").removeClass("droppable-below"); } else { $(this).removeClass("droppable-above").addClass("droppable-below"); } }, out: function(event, ui) { $(this).removeClass("droppable-above").removeClass("droppable-below"); }, drop: function(event, ui) { $(this).removeClass("droppable-above").removeClass("droppable-below"); if (/* mouse is in top half of row */) { // Add new row above the dropped row } else { // Add new row below the dropped row } } }); 

CSS样式会像……

 droppable-above { border-top: solid 3px Blue; } droppable-below { border-bottom: solid 3px Blue; } 

正如你所说, over (就像它的对手一样)只能在droppable上提出一次。 另一方面,每次鼠标移动时都会引发拖动drag事件,并且似乎适合该任务。 但是,这个策略存在两个问题:

  • 无论draggable是否实际位于droppable上,都会引发拖动
  • 即使在这种情况下,droppable也不会传递给事件处理程序。

解决这两个问题的一种方法是使用jQuery的data()工具将overpable和draggable关联在over handler中,并在outdrop处理程序中取消关联:

 $("tr.droppable").droppable({ over: function(event, ui) { if (/* mouse is in top half of row */) { $(this).removeClass("droppable-below") .addClass("droppable-above"); } else { $(this).removeClass("droppable-above") .addClass("droppable-below"); } ui.draggable.data("current-droppable", $(this)); // Associate. }, out: function(event, ui) { ui.draggable.removeData("current-droppable"); // Break association. $(this).removeClass("droppable-above droppable-below"); }, drop: function(event, ui) { ui.draggable.removeData("current-droppable"); // Break association. $(this).removeClass("droppable-above droppable-below"); if (/* mouse is in top half of row */) { // Add new row above the dropped row. } else { // Add new row below the dropped row. } } }); 

既然draggable知道它正在躺着的droppable,我们可以在drag事件处理程序中更新元素的外观:

 $(".draggable").draggable({ drag: function(event, ui) { var $droppable = $(this).data("current-droppable"); if ($droppable) { if (/* mouse is in top half of row */) { $droppable.removeClass("droppable-below") .addClass("droppable-above"); } else { $droppable.removeClass("droppable-above") .addClass("droppable-below"); } } } }); 

下面的代码是一个简单的测试用例,它演示了这个解决方案(它基本上填补了上面的注释空白,并将常见模式重构为辅助函数)。 可放置的设置比前一个示例中的更复杂,主要是因为新创建的表行必须像其兄弟一样可以放置。

你可以在这个小提琴中看到结果。

HTML:

 
New item 1
New item 2
New item 3
New item 4
New item 5

Drag the items above into the table below.

Item 1
Item 2
Item 3
Item 4
Item 5

CSS:

 p { line-height: 32px; } table { width: 100%; } .draggable { background-color: #d0ffff; border: 1px solid black; cursor: pointer; padding: 6px; } .droppable { background-color: #ffffd0; border: 1px solid black; } .droppable td { padding: 10px; } .droppable-above { border-top: 3px solid blue; } .droppable-below { border-bottom: 3px solid blue; } 

使用Javascript:

 $(document).ready(function() { $(".draggable").draggable({ helper: "clone", drag: function(event, ui) { var $droppable = $(this).data("current-droppable"); if ($droppable) { updateHighlight(ui, $droppable); } } }); initDroppable($(".droppable")); function initDroppable($elements) { $elements.droppable({ over: function(event, ui) { var $this = $(this); updateHighlight(ui, $this); ui.draggable.data("current-droppable", $this); }, out: function(event, ui) { cleanupHighlight(ui, $(this)); }, drop: function(event, ui) { var $this = $(this); cleanupHighlight(ui, $this); var $new = $this.clone().children("td:first") .html(ui.draggable.html()).end(); if (isInUpperHalf(ui, $this)) { $new.insertBefore(this); } else { $new.insertAfter(this); } initDroppable($new); } }); } function isInUpperHalf(ui, $droppable) { var $draggable = ui.draggable || ui.helper; return (ui.offset.top + $draggable.outerHeight() / 2 <= $droppable.offset().top + $droppable.outerHeight() / 2); } function updateHighlight(ui, $droppable) { if (isInUpperHalf(ui, $droppable)) { $droppable.removeClass("droppable-below") .addClass("droppable-above"); } else { $droppable.removeClass("droppable-above") .addClass("droppable-below"); } } function cleanupHighlight(ui, $droppable) { ui.draggable.removeData("current-droppable"); $droppable.removeClass("droppable-above droppable-below"); } }); 

我遇到了同样的问题,一直在考虑我将分享的两个解决方案,以防他们指出那些发现这种相对罕见的需求的人。

  • 两个div解决方案:在行的每个单元格中添加两个div,定位为堆叠,每个50%高和全宽,z-index设置为-1,以防止UI干扰。 现在制作这些droppable并使用它们的’over’和’out’事件来切换父单元格或行的类。

  • 放弃可放置并滚动您自己的碰撞检测:编写您自己的碰撞检测以模拟可放置的效果。 这将提供更多的自由,但会导致一些严肃的编码,因此不适合随意的要求。 也容易受到性能问题的影响。 也就是说,应该有一些明显的基于案例的快捷方式对你有利。

我很想知道任何其他低代码解决方案的方法。

我刚刚遇到这个问题。 如果您只是在’drag’事件中实现命中测试,则不需要太多。 在这里,我刚刚使用.myDropTarget标记了所有的放置目标,因此很容易找到它们,循环遍历它们并检查鼠标是否在它们之上。

像这样的东西:

 thingToBeDragged.draggable({ drag: function(evt) { $('.myDropTarget').removeClass('topHalf bottomHalf').each(function() { var target = $(this), o = target.offset(), x = evt.pageX - o.left, y = evt.pageY - o.top; if (x > 0 && y > 0 && x < target.width() && y < target.height()) { // mouse is over this drop target, but now you can get more // particular: is it in the top half, bottom half, etc. if (y > target.height() * 0.5) { target.addClass('topHalf'); } else { target.addClass('bottomHalf'); } } }); }, stop: function() { var droppedOn = $('.topHalf, .bottomHalf'); } }); 

另一种方法是在提示元素中添加一个类或其他选举器。 然后在可拖动的定义中,在拖动处理程序上,更新提示位置:

 $('#dropArea').droppable({ over: function(event, ui) // Create a clone 50 pixels above and 100 to the left of drop area $('#someHint').clone() .css({ position: 'absolute', top: ui.offset.top+50, left: ui.offset.left-50 }) .addClass("clone") // Mark this as a clone, for hiding on drop or out .addClass("dragHelper") // Mark this as a hint, for moving with drag .appendTo(document.body) }, out: function(event, ui) { $('.clone').remove(); // Remove any hints showing }, drop: function(event, ui) { $('.clone').remove(); // Remove any hints showing } }); $("#mydiv").draggable({ drag: function(event, ui) { $('.dragHelper').css('left',ui.offset.left); $('.dragHelper').css('top',ui.offset.top); } }); 

这是一个相当粗略(无代码)的解决方案,但您可以尝试在Droppable中使用hoverClass选项并创建一个名为“hovering”的新类来设置Droppable行为,该行为仅在Draggablehover在Droppable上时才会发生。 这个“hover”类可以(这是粗略的)运行某种无限循环或其他类型的检查器; 我之前没有使用过这些课程,所以在过去的这一点上我想不出更多细节。 = /

编辑:甚至更粗糙,您可以使用“hover”类交替禁用和启用Droppable; 我肯定会同步这样做,并且还有慷慨的时间划分。 交替的禁用和启用调用应该触发其中一个事件,尽管您需要通过哪个事件来查找。