Javascript jQuery droppable - 在拖拽期间接收事件(不仅仅是在初始拖拽时)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6199890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery droppable - receiving events during drag over (not just on initial drag over)
提问by Richard Ev
I am using jQuery droppable(in conjunction with jQuery draggable) to allow the user to add rows to an HTML table by dragging items from a list and dropping them on the table.
我正在使用jQuery droppable(与jQuery draggable结合使用)来允许用户通过从列表中拖动项目并将它们放在表格上来向 HTML 表格添加行。
This works well, however at present the logic is that when the user drag-drops on a table row the new row gets added belowthe row they dropped on.
这很有效,但是目前的逻辑是,当用户在表行上拖放时,新行会添加到他们所放行的下方。
It would be better if the new row's add position was based on whether the user dropped in the upper or lower half of an existing row.
如果新行的添加位置基于用户是放在现有行的上半部分还是下半部分,那就更好了。
This is easy enough to calculate in the drop
event, but I need to give UI feedback as the user drags (which I would do by means of two CSS classes droppable-above
and droppable-below
for example).
这是很容易的在计算drop
事件,但我需要给UI反馈用户拖动(我会用两个CSS类的方式做droppable-above
,并droppable-below
举例)。
This doesn't seem to be possible, as the over
event only fires once; when the user initially drags over the droppable element.
这似乎不可能,因为over
事件只触发一次;当用户最初拖动可放置元素时。
Is it possible to get the over
event to fire for every mouse move while the user is over a droppable element?
是否有可能获得over
事件火每鼠标移动,而用户是在投掷的元素?
If so, then I'd be able to do this:
如果是这样,那么我可以这样做:
$("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
}
}
});
The CSS styles would be something like...
CSS 样式将类似于...
droppable-above { border-top: solid 3px Blue; }
droppable-below { border-bottom: solid 3px Blue; }
回答by Frédéric Hamidi
As you said, over
(like its counterpart out
) is only raised once on the droppable. On the other hand, the drag
event of the draggableis raised every time the mouse moves, and seems appropriate for the task. There are, however, two problems with this strategy:
正如你所说,over
(就像它的对应物out
)在 droppable 上只提升一次。另一方面,每次鼠标移动时都会引发可拖动的drag
事件,并且似乎适合该任务。但是,这种策略存在两个问题:
drag
is raised whether or not the draggable actually lies over a droppable,- even in that case, the droppable is not passed to the event handler.
drag
无论可拖动对象是否确实位于可放置对象上,都会引发,- 即使在这种情况下,droppable 也不会传递给事件处理程序。
One way to solve both problems is to associate the droppable and the draggable in the over
handler, using jQuery's data()facility, and disassociate them in the out
and drop
handlers:
要解决这两个问题的方法之一是相关联的可放开和可拖动的over
处理程序,使用jQuery的数据()工厂,并在撇清他们out
和drop
处理程序:
$("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.
}
}
});
Now that the draggable knows the droppable it's lying over, we can update the element's appearance in a drag
event handler:
既然可拖动对象知道它所在的可放置对象,我们可以在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");
}
}
}
});
The code that follows is a simple test case that demonstrates this solution (it basically fills the commented gaps above and refactors common patterns into helper functions). The droppable setup is a little more intricate than in the previous example, mainly because the newly created table rows have to be made droppable like their siblings.
下面的代码是一个简单的测试用例,演示了这个解决方案(它基本上填补了上面注释的空白,并将常见模式重构为辅助函数)。droppable 设置比前面的例子稍微复杂一些,主要是因为新创建的表行必须像它们的兄弟一样可放置。
You can see the results in this fiddle.
你可以在这个 fiddle 中看到结果。
HTML:
HTML:
<div class="draggable">New item 1</div>
<div class="draggable">New item 2</div>
<div class="draggable">New item 3</div>
<div class="draggable">New item 4</div>
<div class="draggable">New item 5</div>
<p>Drag the items above into the table below.</p>
<table>
<tr class="droppable"><td>Item 1</td></tr>
<tr class="droppable"><td>Item 2</td></tr>
<tr class="droppable"><td>Item 3</td></tr>
<tr class="droppable"><td>Item 4</td></tr>
<tr class="droppable"><td>Item 5</td></tr>
</table>
CSS:
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:
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");
}
});
回答by James Evason
I am hitting the same issue and have been thinking about two solutions which I will share in case they give direction to others who find this relatively rare need.
我遇到了同样的问题,并且一直在考虑两种解决方案,我将分享这些解决方案,以防它们为发现这种相对罕见需求的其他人提供指导。
Two div solution: Add two divs into each cell of the row, positioned to be stacked and each 50% high and full width with z-index set to -1 to protect from UI interference. Now make these droppables and use their 'over' and 'out' events to toggle the classes of the parent cell or row.
Abandon droppable and roll your own collision detection: Write your own collision detection to mimic the droppable effect. This which would give more freedom but would lead to some serious coding and so is not for the casual requirement. Would also be susceptible to performance issues. That said, there should be some obvious case-based shortcuts that would work in your favour.
两个 div 解决方案:在行的每个单元格中添加两个 div,定位为堆叠,每个 50% 高和全宽,z-index 设置为 -1 以防止 UI 干扰。现在制作这些 droppables 并使用它们的 'over' 和 'out' 事件来切换父单元格或行的类。
放弃 droppable 并滚动您自己的碰撞检测:编写您自己的碰撞检测以模拟 droppable 效果。这将提供更多的自由,但会导致一些严重的编码,因此不适合随意的要求。也容易受到性能问题的影响。也就是说,应该有一些明显的基于案例的快捷方式对您有利。
I would be interested to hear of any other approaches to low-code solution.
我很想知道低代码解决方案的任何其他方法。
回答by Daniel Earwicker
I just hit this problem. Not much is required if you just implement hit testing in the 'drag' event. Here I've just tagged all my drop targets with .myDropTarget
, so it's easy to find them all, loop through them and check whether the mouse is over them.
我刚遇到这个问题。如果您只是在 'drag' 事件中实现命中测试,则不需要太多。在这里,我刚刚用 标记了我所有的放置目标.myDropTarget
,因此很容易找到它们,遍历它们并检查鼠标是否在它们上方。
Something like this:
像这样的东西:
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');
}
});
回答by NoelHunter
Another method is to add a class or other elector to the hint element. Then in the draggable definition, on the drag handler, update the hint position:
另一种方法是向提示元素添加一个类或其他选举人。然后在可拖动定义中,在拖动处理程序上,更新提示位置:
$('#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);
}
});
回答by sichinumi
This is a rather crude (and codeless) solution, but you could try using the hoverClass option with your Droppable and creating a new class called "hovering" to set Droppable behavior that only happens when the Draggable is hovering over the Droppable. This "hovering" class could then (this is the crude bit) run some sort of endless loop or some other sort of checker; I haven't used these classes before, so I can't think of any more specifics past this point. =/
这是一个相当粗糙(且无代码)的解决方案,但您可以尝试将 hoverClass 选项与 Droppable 一起使用,并创建一个名为“hovering”的新类来设置仅在 Draggable 悬停在 Droppable 上时才会发生的 Droppable 行为。然后,这个“悬停”类可以(这是粗略的)运行某种无限循环或某种其他类型的检查器;我以前没有使用过这些类,所以在这一点上我想不出更多细节。=/
Edit: Even cruder, you could alternate disabling and enabling the Droppable using the "hovering" class; I would definitelydo this synchronously though, and with a generous time delineation as well. The alternating disable and enable calls shouldtrigger one of the events, though which one you'll have to experiment with to find out.
编辑:更粗暴的是,您可以使用“悬停”类交替禁用和启用 Droppable;不过,我肯定会同步执行此操作,并且还会有大量的时间划分。交替禁用和启用调用应触发其中一个事件,但您必须尝试找出哪个事件。