JQuery UI - перетащить событие "привязать"

Я ищу способ привязать событие snap.

Когда я перетаскиваю элемент поверх своей поверхности, а перетаскиваемый элемент привязывается к объявленной позиции привязки, я хочу вызвать событие.

Что-то вроде этого:

$(".drag").draggable({
  snap: ".grid",
  snaped: function( event, ui ) {}
});

Бонусная точка: со ссылкой на элемент .grid, где был перехвачен элемент перетаскивания.

Ответ 1

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

Из этого вопроса, мы знаем, что виджет хранит массив потенциально "спящих" элементов в свойстве snapElements. В свою очередь, каждый элемент в этом массиве предоставляет свойство snapping, которое является true, если в данный момент привязанный к перехвату помощник привязан к этому элементу и false в противном случае (помощник может привязать к нескольким элементам одновременно).

Массив snapElements обновляется для каждого события drag, поэтому он всегда обновляется в обработчиках drag. Оттуда нам нужно только получить экземпляр виджета draggable из связанного элемента с data() и вызвать его метод _trigger() чтобы поднять наше собственное событие snapped (фактически dragsnapped под капотом). Попутно мы можем $. Extend() объект ui с объектом jQuery, обертывающим отрезанный элемент:

$(".drag").draggable({
    drag: function(event, ui) {
        var draggable = $(this).data("draggable");
        $.each(draggable.snapElements, function(index, element) {
            if (element.snapping) {
                draggable._trigger("snapped", event, $.extend({}, ui, {
                    snapElement: $(element.item)
                }));
            }
        });
    },
    snap: ".grid",
    snapped: function(event, ui) {
        // Do something with 'ui.snapElement'...
    }
});

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

К счастью, массив snapElements является постоянным, поэтому мы можем использовать его для хранения состояния. Мы можем добавить свойство snappingKnown к каждому элементу массива, чтобы отслеживать, что мы уже вызвали событие snapped для этого элемента. Более того, мы можем использовать его для обнаружения того, что элемент был отключен со времени последнего вызова и соответствующим образом реагирует.

Обратите внимание, что вместо того, чтобы вводить другое событие snapped-out, приведенный ниже код решает передать дополнительное свойство snapping (отражающее текущее состояние элемента) в объекте ui (что, конечно, является вопросом только предпочтение):

$(".drag").draggable({
    drag: function(event, ui) {
        var draggable = $(this).data("draggable");
        $.each(draggable.snapElements, function(index, element) {
            ui = $.extend({}, ui, {
                snapElement: $(element.item),
                snapping: element.snapping
            });
            if (element.snapping) {
                if (!element.snappingKnown) {
                    element.snappingKnown = true;
                    draggable._trigger("snapped", event, ui);
                }
            } else if (element.snappingKnown) {
                element.snappingKnown = false;
                draggable._trigger("snapped", event, ui);
            }
        });
    },
    snap: ".grid",
    snapped: function(event, ui) {
        // Do something with 'ui.snapElement' and 'ui.snapping'...
        var snapper  = ui.snapElement.attr("id"),snapperPos = ui.snapElement.position(),
            snappee  = ui.helper.attr("id"),     snappeePos = ui.helper.position(),
            snapping = ui.snapping;
        // ...
    }
});

Вы можете протестировать это решение здесь.

В заключение, еще одно улучшение может заключаться в том, чтобы событие snapped было отменено, как событие drag. Для этого нам нужно вернуть false из нашего обработчика drag, если один из вызовов _trigger() возвращает false. Вы можете подумать дважды, прежде чем выполнять это, однако, поскольку отмена операции перетаскивания при оснастке или оснастке не похожа на очень удобную для пользователя функцию в общем случае.

Обновление: Начиная с jQuery UI 1.9, ключ data() становится полнофункциональным именем виджета, с замененными точками на черточки. Соответственно, код, используемый выше для получения экземпляра виджета, становится следующим:

var draggable = $(this).data("ui-draggable");

Вместо:

var draggable = $(this).data("draggable");

Использование неквалифицированного имени по-прежнему поддерживается в 1.9, но устарело, а поддержка будет уменьшена в 1.10.

Ответ 2

В jquery-ui 1.10.0 приведенный выше код не работает. Вместо этого функция перетаскивания:

drag: function(event, ui) {
  var draggable = $(this).data("ui-draggable")
  $.each(draggable.snapElements, function(index, element) {
    if(element.snapping) {
      draggable._trigger("snapped", event, $.extend({}, ui, {
        snapElement: $(element.item)
      }));
    }
  });
}