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 должны вызвать одно из событий, хотя какой из них вам придется экспериментировать, чтобы узнать.