我正在使用jQuery droppable(与jQuery draggable一起使用)允许用户通过从列表中拖动项目并将其放在桌面上来向HTML表格添加行。
这很好用,但是目前的逻辑是,当用户在表格行上拖放时,新行会在下面添加他们删除的行。
如果新行的添加位置取决于用户是否放入现有行的上半部分或下半部分,那会更好。
这很容易在drop
事件中计算,但我需要在用户拖动时提供UI反馈(我将通过两个CSS类droppable-above
和{{1}进行操作例如)。
这似乎不可能,因为droppable-below
事件只触发一次;当用户最初拖动可放置元素时。
当用户在可放置元素上时,是否可以为每次鼠标移动触发over
事件?
如果是这样,那么我就能做到这一点:
over
CSS样式将类似......
$("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
}
}
});
答案 0 :(得分:28)
正如你所说,over
(就像它的对应out
)只在droppable上提出一次。另一方面,每次鼠标移动时都会引发 draggable 的drag
事件,并且似乎适合该任务。但是,这个策略存在两个问题:
drag
都会被提升
解决这两个问题的一种方法是使用jQuery的data()工具将droppable和draggable关联到over
处理程序中,并在out
和{{1}中取消关联它们处理程序:
drop
现在draggable知道它所处的droppable,我们可以在$("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.
}
}
});
事件处理程序中更新元素的外观:
drag
下面的代码是一个简单的测试用例,它演示了这个解决方案(它基本上填补了上面的注释空白并将常见模式重构为辅助函数)。可放置的设置比前一个示例更复杂,主要是因为新创建的表行必须像兄弟姐妹一样可以放置。
您可以在this fiddle中看到结果。
<强> HTML:强>
$(".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");
}
}
}
});
<强> CSS:强>
<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>
<强>使用Javascript:强>
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;
}
答案 1 :(得分:2)
我遇到了同样的问题,并且一直在思考我将分享的两个解决方案,以便他们为发现这种相对罕见需求的其他人指明方向。
两个div解决方案:在行的每个单元格中添加两个div,定位为堆叠,每个50%高和全宽,z-index设置为-1,以防止UI干扰。现在制作这些droppable并使用它们的'over'和'out'事件来切换父单元格或行的类。
放弃droppable并滚动您自己的碰撞检测:编写您自己的碰撞检测以模拟可放置的效果。这将提供更多的自由,但会导致一些严肃的编码,因此不适合随意的要求。也容易受到性能问题的影响。也就是说,应该有一些明显的基于案例的快捷方式对你有利。
我很想知道任何其他低代码解决方案。
答案 2 :(得分:1)
我刚刚遇到这个问题。如果您只是在'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');
}
});
答案 3 :(得分:1)
另一种方法是在提示元素中添加一个类或其他元素。然后在可拖动的定义中,在拖动处理程序上,更新提示位置:
$('#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);
}
});
答案 4 :(得分:0)
这是一个相当粗略(无代码)的解决方案,但您可以尝试将hoverClass选项与Droppable一起使用并创建一个名为“hovering”的新类来设置Droppable行为,该行为仅在Draggable悬停在Droppable上时才会发生。这个“悬停”类可以(这是粗略的)运行某种无限循环或其他类型的检查器;我之前没有使用过这些课程,所以在过去的这一点上,我想不出更具体的内容。 = /
编辑:甚至比较粗糙,你可以使用“悬停”类来交替禁用和启用Droppable;我会绝对同步这样做,并且还有慷慨的时间划分。交替的禁用和启用调用应该触发其中一个事件,但您必须尝试查找哪个事件。