JQuery droppable и прокручиваемые divs

У меня небольшая проблема с jQuery UI droppable, но я не совсем уверен, есть ли у меня эта проблема из-за моего кода или из-за ошибки в компоненте.

У меня есть div с фиксированной шириной и высотой. Переполнение-x для этого div устанавливается в скрытое, overflow-y устанавливается в auto. Внутри этого div у меня есть еще несколько div. Так много из них, что внешний div начинает прокрутку. Каждый из внутренних divs является droppable, принимая перетаскиваемый, который находится за пределами оболочки div.

Если я перетаскиваю перетаскиваемый элемент где-то внутри оболочки, все работает нормально. Проблема в том, что событие drop становится даже срабатывающим, если я удаляю элемент чуть ниже оболочки обертки.

Я не очень хорошо объясняю проблему; поэтому здесь приведен код, который воспроизводит проблему:

http://jsfiddle.net/2p56Y/

Просто перетащите "Drag Me!". контейнер ниже div с полосой прокрутки. Неожиданно вы увидите предупреждение "сброшено".

Теперь что-то интересное: если вы перейдете к пункту "Test28", и теперь вы перетаскиваете перетаскиваемый под оболочкой, событие drop не запускается. Похоже, что скрытые элементы все еще доступны, когда вы бросаете что-то на них.

Итак, это ошибка, или мне нужно написать свой код по-другому, чтобы заставить его работать? (или оба?:-))

Ответ 1

Проверяйте границы элемента droppable на родительский контейнер и прерывайте выполнение функции, если нижняя часть droppable находится выше родительской вершины или верхняя часть droppable находится ниже нижней части родителя:

$('.item').droppable( {
    activeClass: "ui-state-default",
    hoverClass: "ui-state-hover",
    accept: "#draggable",
    drop: function( event, ui ) {
        var cTop = $(this).closest(".box").position().top + parseInt($(this).closest(".box").css("margin-top"));
        var cBtm = $(this).closest(".box").position().top + parseInt($(this).closest(".box").css("margin-top")) + $(this).closest(".box").height();
        var dTop = $(this).position().top + parseInt($(this).css("margin-top"));
        var dBtm = $(this).position().top + parseInt($(this).css("margin-top")) + $(this).height()

        if (dBtm > cTop && dTop < cBtm) {
            alert("Dropped.");
        }
    }
});

Пример: http://jsfiddle.net/lthibodeaux/2p56Y/6/

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

Ответ 2

Вы уже приняли ответ, хотя я подумал, что я должен просто поместить его туда:

Более элегантное обходное решение (основанное на пузырьках событий и которое действительно имеет дело только с видимостью на один уровень, а не рекурсивно) может быть выполнено путем создания $('.box, .item').droppable(), а поскольку по умолчанию greedy:false должно произойти событие вложенного события div, внешним div.

Простая проверка элемента, например hasClass('box'), означает, что произошел сбой в допустимой области, поэтому все, что вам нужно сделать, - это включить в кеш-кеш для внутреннего кэша элемент, который был удален, а затем на внешнее событие drop div ( что происходит, как уже упоминалось, только через мгновение) делать с ним что угодно.

Если вы выйдете за пределы внешнего div, несмотря на то, что будет происходить внутреннее событие удаления div, внешнее не будет, поэтому ничего, кроме бесполезного события кеширования, не произойдет. Единственная проблема заключается в том, что он выглядит как ошибка с не-жадными вложенными droppables, пример jQuery http://jqueryui.com/demos/droppable/propagation.html даже не работает должным образом me - он ведет себя так, как будто он использует захват событий, а не пузырь событий...

Единственное другое (по общему признанию, гораздо более правдоподобное) объяснение заключается в том, что я не понимаю, как должны вести себя вложенные droppables.

Ответ 3

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

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

ПРОБЛЕМА (В ТЕХНИЧЕСКИХ УСЛОВИЯХ)

Если вы просмотрите OP jsfiddle с помощью своих инструкций, вы заметите, что оба этих элемента DOM независимы друг от друга и живут на одном уровне DOM. Однако, как отмечают OP и некоторые другие, jQuery UI Droppable, похоже, пузырится на событие drop на элементы DOM с переполняющим содержимым, прикрывающим целевой droppable.

РЕШЕНИЕ

Установите флаг, когда элемент отбрасывается в целевой droppable. Проверьте флаг в событии drop подпадающего элемента DOM. Если этот флаг содержит значение, указывающее, что он был удален в целевом droppable, верните false, ничего не делайте или не возвращайте. Независимо от того, что вы хотите сделать, чтобы игнорировать событие drop.

В моем конкретном случае я устанавливаю свой флаг с помощью простого значения атрибута в ui.helper в целевом событии droppable drop, например:

$(ui.helper).attr('ignore-drop-event', "true");

В событии с барботированным перетаскиванием в нижней подкачке я проверяю, что для этого атрибута установлено значение true:

if ($(ui.helper).attr('ignore-drop-event') === "true") {
    // Ignore this drop event since it occurred as part of event bubbling
}
else {
    // This is a "real" drop event
}

Это решение основывается на предположении, что

  • Порядок барботирования событий согласован. Значение целевого droppable всегда будет принимать событие до элемента droppable underlapping. Я не знаю, верно ли это, но эта оценка была достоверной при тестировании.

  • Значение атрибута определено до проверки флажка в событии с барботируемым событием.

Надеюсь, это поможет кому-то другому.