jQuery droppable-получение событий во время перетаскивания (не только при первоначальном перетаскивании)
Я использую jQuery droppable (вместе с jQuery draggable), чтобы пользователь мог добавлять строки в таблицу HTML, перетаскивая элементы из списка и отбрасывая их на таблицу.
это хорошо работает, однако в настоящее время логика заключается в том, что когда пользователь перетаскивает строку таблицы, новая строка добавляется ниже ряд, на который они упали.
было бы лучше, если бы позиция добавления новой строки основывалась на том, пользователь упал в верхней или нижней половине существующей строки.
это достаточно легко рассчитать в drop
событие, но мне нужно дать обратную связь UI, поскольку пользователь перетаскивает (что я бы сделал с помощью двух классов CSS droppable-above
и droppable-below
например).
это кажется невозможным, так как over
событие срабатывает только один раз; когда пользователь изначально перетаскивает элемент droppable.
можно ли получить 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; }
5 ответов
как вы сказали, over
(как и его коллега out
) поднимается только один раз на droppable. С другой стороны,drag
событие разрешить вызывается каждый раз, когда мышь движется, и кажется подходящим для задачи. Однако у этой стратегии есть две проблемы:--16-->
-
drag
поднимается, действительно ли перетаскиваемое лежит над сбрасываемым, - даже в этом случае droppable не передается событию обработчик.
один из способов решения обеих проблем-связать droppable и draggable в over
обработчик, используя jQuery data () объект, и разъединить их в 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.
}
}
});
теперь, когда перетаскиваемый знает, что он лежит, мы можем обновить внешний вид элемента в 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 немного сложнее, чем в предыдущем примере, главным образом потому, что вновь созданные строки таблицы должны быть сделаны droppable как их братья и сестры.
вы можете увидеть результаты в этот скрипка.
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");
}
});
Я сталкиваюсь с той же проблемой и думал о двух решениях, которые я поделюсь, если они дадут направление другим, которые находят эту относительно редкую потребность.
решение Two div: добавьте два divs в каждую ячейку строки, расположенную для штабелирования, и каждая 50% высокая и полная ширина с Z-индексом, установленным в -1 для защиты от помех пользовательского интерфейса. Теперь сделайте эти droppables и используйте их события "over" и " out " для переключения классов родительской ячейки или строки.
отказаться от droppable и свернуть собственное обнаружение столкновений: напишите свое собственное обнаружение столкновений, чтобы имитировать эффект droppable. Это дало бы больше свободы, но привело бы к серьезному кодированию и поэтому не является случайным требованием. Будет также подвержен проблемам производительности. Тем не менее, должны быть некоторые очевидные ярлыки, основанные на конкретных случаях, которые будут работать в вашу пользу.
Мне было бы интересно услышать о любом другом подходы к решению с низким кодом.
Я просто попал в эту проблему. Не так много требуется, если вы просто реализуете тестирование попадания в событии "перетаскивания". Здесь я только что пометил все мои цели.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);
}
});
Это довольно грубое (и бескодовое) решение, но вы можете попробовать использовать опцию hoverClass с вашим Droppable и создать новый класс под названием "зависание", чтобы установить поведение Droppable, которое происходит только тогда, когда перетаскиваемый парит над Droppable. Этот" зависающий " класс может затем (это грубый бит) запустить какой-то бесконечный цикл или какой-то другой вид проверки; я не использовал эти классы раньше, поэтому я не могу думать о каких-либо других особенностях за этим пунктом. =/
изменить: Еще грубее, вы можете чередовать отключение и включение Droppable, используя класс "зависания"; я бы наверняка сделайте это синхронно, хотя, и с щедрым разграничением времени, а также. Чередующиеся вызовы disable и enable должны вызвать одно из событий, хотя какой из них вам придется экспериментировать, чтобы узнать.