cancel - отмена операции

Dragula — это легковесная библиотека для реализации перетаскивания элементов между контейнерами и внутри одного контейнера. Одной из важных возможностей Dragula является управление отменой операции перетаскивания. Механизм cancel позволяет предотвратить начало перетаскивания для определённых элементов на основании заданных условий.

Основной принцип работы cancel

Свойство cancel принимает функцию, которая вызывается при попытке перетащить элемент. Функция получает один аргумент — DOM-элемент, на который пользователь навёл курсор для начала перетаскивания. Если функция возвращает true, Dragula не начнёт процесс drag для этого элемента.

const drake = dragula([container1, container2], {
    cancel: function (el) {
        return el.classList.contains('no-drag');
    }
});

В этом примере элементы с классом no-drag не подлежат перетаскиванию.

Ключевые моменты:

  • cancel вызывается только при начале перетаскивания, до фактического создания ghost-элемента.
  • Если вернуть false или ничего не вернуть, Dragula позволит элементу перетаскиваться.
  • Функция cancel может использовать любые свойства элемента: классы, атрибуты, data-атрибуты, вложенные элементы.

Использование с вложенными элементами

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

const drake = dragula([container1, container2], {
    cancel: function (el, handle) {
        // el — перетаскиваемый элемент
        // handle — элемент, с которого был инициирован drag
        return handle.tagName === 'BUTTON';
    }
});

Здесь перетаскивание не начнётся, если пользователь нажал на кнопку внутри элемента. Аргумент handle позволяет точнее контролировать, какой именно узел инициирует drag.

Комбинация с moves

Dragula предоставляет ещё одну функцию moves, которая также управляет доступностью перетаскивания. Отличие cancel от moves:

  • cancel отменяет уже начатую попытку drag на ранней стадии.
  • moves разрешает или запрещает drag в зависимости от позиции курсора или состояния элемента.

Пример комбинированного использования:

const drake = dragula([container1, container2], {
    cancel: '.no-drag',
    moves: function (el, source, handle, sibling) {
        // Можно запретить перетаскивать только элементы нечетных индексов
        const index = Array.from(source.children).indexOf(el);
        return index % 2 === 0;
    }
});

В этом случае элементы с классом no-drag полностью исключаются из drag, а среди остальных drag возможен только для элементов с чётным индексом.

Практическое применение

  1. Отключение drag для служебных кнопок. При создании интерфейсов с карточками часто внутри есть кнопки «удалить», «редактировать». Использование cancel предотвращает случайное перемещение карточки при клике на эти кнопки.

  2. Блокировка drag для определённых категорий элементов. Если нужно, чтобы некоторые элементы в списке были фиксированными, достаточно добавить им определённый класс и вернуть true в cancel.

  3. Динамическая отмена перетаскивания. cancel может проверять текущее состояние приложения: например, если элемент заблокирован в базе данных, возвращать true для отмены drag.

const drake = dragula([container1, container2], {
    cancel: function(el) {
        return el.dataset.locked === 'true';
    }
});

Важные нюансы

  • Событие drag не запускается, если cancel вернул true. Это значит, что нельзя подписаться на события drag для элементов, отменённых через cancel.
  • Можно использовать селекторы CSS вместо функции:
const drake = dragula([container1, container2], {
    cancel: '.non-draggable'
});

В этом случае Dragula автоматически запрещает перетаскивание для всех элементов с указанным селектором.

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

cancel является мощным инструментом управления поведением drag-and-drop, обеспечивая точное ограничение того, какие элементы могут быть перемещены, а какие должны оставаться фиксированными.