jQuery droppable - получение событий во время перетаскивания (не только при первоначальном перетаскивании) - PullRequest
23 голосов
/ 01 июня 2011

Я использую jQuery droppable (в сочетании с jQuery draggable ), чтобы позволить пользователю добавлять строки в таблицу HTML, перетаскивая элементы из списка и помещая их в таблицу.

Это работает хорошо, однако в настоящее время логика такова, что когда пользователь перетаскивает строку таблицы, новая строка добавляется ниже строки, на которую они выпали.

Было бы лучше, если бы позиция добавления новой строки была основана на том, опустил ли пользователь верхнюю или нижнюю половину существующей строки.

Это достаточно легко вычислить в событии drop, ноМне нужно дать отзыв об интерфейсе пользователя, когда пользователь перетаскивает (что я бы сделал с помощью двух классов CSS droppable-above и droppable-below, например).

Это не представляется возможным, так как over событие происходит только один раз;когда пользователь вначале перетаскивает элемент «перетаскивание».

Возможно ли, чтобы событие 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 ]

29 голосов
/ 22 июня 2011

Как вы сказали, over (как и его аналог out) поднимается только один раз на сброс. С другой стороны, событие drag перетаскиваемого вызывается каждый раз, когда движется мышь, и кажется подходящим для этой задачи. Однако у этой стратегии есть две проблемы:

  • drag повышается вне зависимости от того, находится ли перетаскиваемый объект над опущенным,
  • даже в этом случае буква не передается в обработчик событий.

Один из способов решения обеих проблем - связать dropbleable и draggable в обработчике over, используя jQuery's 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");
            }
        }
    }
});

Следующий код представляет собой простой тестовый пример, демонстрирующий это решение (он в основном заполняет закомментированные пробелы выше и преобразовывает общие шаблоны в вспомогательные функции). Параметр «Сбрасываемый» немного сложнее, чем в предыдущем примере, главным образом потому, что вновь созданные строки таблицы должны быть сбрасываемыми, как их братья и сестры.

Вы можете увидеть результаты в этой скрипке .

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");
    }
});
2 голосов
/ 17 ноября 2011

Я затрагиваю ту же проблему и думаю о двух решениях, которыми я поделюсь, если они дадут указание другим, которые находят эту относительно редкую потребность.

  • Решение для двух делений: добавьте два деления в каждую ячейку строки, расположите их в стеке, и каждый на 50% высотой и полной шириной с z-index, установленным в -1, для защиты от пользовательского интерфейсавмешательство.Теперь создайте эти droppables и используйте их события «over» и «out» для переключения классов родительской ячейки или строки.

  • Отмените бросание и бросьте свое собственное обнаружение столкновений: напишите свое собственное обнаружение столкновений, чтобы имитировать эффект сбрасывания.Это дало бы больше свободы, но привело бы к серьезному кодированию и не для случайного требования.Также будет подвержен проблемам с производительностью.Тем не менее, должны быть некоторые очевидные сочетания клавиш, которые будут работать в вашу пользу.

Мне было бы интересно услышать о любых других подходах к решению с низким кодом.

1 голос
/ 09 июля 2014

Другой метод - добавить класс или другой выборщик к элементу подсказки.Затем в перетаскиваемом определении в обработчике перетаскивания обновите позицию подсказки:

$('#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);
        }

});
1 голос
/ 14 мая 2012

Я только что столкнулся с этой проблемой. Ничего особенного не требуется, если вы просто реализуете проверку попадания в событии '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');
    }
});
0 голосов
/ 02 июня 2011

Это довольно грубое (и без кода) решение, но вы можете попробовать использовать параметр hoverClass с Droppable и создать новый класс с именем "hovering", чтобы установить поведение Droppable, которое происходит, только когда Draggable находится над Droppable.Этот «зависший» класс может затем (это грубый бит) запустить какой-то бесконечный цикл или какой-то другой вид проверки;Я не использовал эти классы раньше, так что я не могу думать о каких-либо дополнительных особенностях этого момента.= /

Редактировать: еще грубее, вы можете чередовать отключение и включение Droppable с помощью класса "зависания";Хотя я бы определенно сделал бы это синхронно, но также и с щедрым определением времени.Переменные вызовы отключения и включения должны вызвать одно из событий, хотя с каким вам придется поэкспериментировать, чтобы выяснить.

...