正如你所说,over
(就像它的对应物一样out
)只在 droppable 上被提升一次。另一方面,每次鼠标移动时都会引发可拖动drag
事件,并且似乎适合该任务。然而,这种策略存在两个问题:
drag
无论可拖动对象实际上是否位于可放置对象上,都会引发,
- 即使在这种情况下,droppable 也不会传递给事件处理程序。
解决这两个问题的一种方法是over
使用 jQuery 的data()out
工具在处理程序中关联 droppable 和 draggable,并在处理程序中取消它们的关联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.
}
}
});
现在可拖动对象知道它所在的可放置对象,我们可以在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");
}
}
}
});
下面的代码是一个简单的测试用例,演示了这个解决方案(它基本上填补了上面注释的空白,并将常见模式重构为辅助函数)。droppable 设置比前面的示例稍微复杂一些,主要是因为必须使新创建的表行像它们的兄弟姐妹一样可丢弃。
你可以在这个 fiddle中看到结果。
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:
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");
}
});