Определите, что перетаскивается из событий dragenter & dragover
Я пытаюсь использовать HTML5 draggable API (хотя я понимаю, что имеет свои проблемы). До сих пор единственный showstopper, с которым я столкнулся, заключается в том, что я не могу определить способ определения того, что перетаскивается, когда срабатывает событие dragover
или dragenter
:
el.addEventListener('dragenter', function(e) {
// what is the draggable element?
});
Я понимаю, что могу предположить, что это последний элемент для запуска события dragstart
, но... multitouch. Я также попытался использовать e.dataTransfer.setData
из dragstart
для присоединения уникального идентификатора, но, по-видимому, данные недоступны из dragover
/dragenter
:
Эти данные будут доступны только после того, как произойдет падение во время события drop.
Итак, любые идеи?
Обновление:. На момент написания этой статьи перетаскивание HTML5, похоже, не реализовано ни в одном крупном мобильном браузере, что на практике указывает на мультитач. Тем не менее, я хотел бы, чтобы решение, которое гарантировало работу над любой реализацией спецификации, которая, как представляется, не позволяет одновременно перетаскивать несколько элементов.
Я разместил рабочее решение ниже, но это уродливое взломать. Я все еще надеюсь на лучший ответ.
Ответы
Ответ 1
Краткий ответ на мой вопрос: Нет. Спецификация WHATWG не предоставляет ссылку на перетаскиваемый элемент (называемый "исходным узлом" в спецификации) в dragenter
, dragover
или dragleave
.
Почему бы и нет? Две причины:
Во-первых, как отмечает Джеффри в своем комментарии, спецификация WHATWG основана на реализации IE5+ перетаскивания, которое предшествовало устройствам с несколькими касаниями. (На момент написания статьи ни в одном крупном мультитач-браузере не реализовывалось перетаскивание HTML.) В контексте "одного касания" легко сохранить глобальную ссылку на текущий перетаскиваемый элемент в dragstart
.
Во-вторых, перетаскивание HTML позволяет перетаскивать элементы по нескольким документам. Это удивительно, но это также означает, что предоставление ссылки на элемент, перетаскиваемый в каждом dragenter
, dragover
или dragleave
, не имеет смысла; Вы не можете ссылаться на элемент в другом документе. Сила API в том, что эти события работают одинаково, независимо от того, происходит ли перетаскивание в том же документе или в другом.
Но неспособность предоставить сериализованную информацию всем событиям перетаскивания, кроме как через dataTransfer.types
(как описано в моем ответе на рабочее решение), является явным упущением в API. Я представил предложение об открытых данных в событиях перетаскивания в WHATWG, и я надеюсь, что вы поддержите его.
Ответ 2
Я хотел добавить здесь очень четкий ответ, чтобы это было очевидно для всех, кто блуждает мимо здесь. Это было сказано несколько раз в других ответах, но здесь это так ясно, как я могу это сделать:
dragover
НЕ ИМЕЕТ ПРАВА, чтобы видеть данные в событии перетаскивания.
Эта информация доступна только во время DRAG_START и DRAG_END (drop).
Проблема в том, что это не очевидно вообще и безумие, пока вы не прочтете достаточно глубоко о спецификации или местах, подобных здесь.
обходным:
В качестве возможного обхода я добавил специальные ключи к объекту DataTransfer и протестировал их. Например, чтобы повысить эффективность, я хотел найти некоторые правила "drop target", когда мое перетаскивание началось, а не каждый раз, когда "перетаскивание" произошло. Для этого я добавил ключи, идентифицирующие каждое правило, на объект dataTransfer и проверил те, которые содержат "содержит".
ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders")
И все такое. Чтобы быть ясным, это "включает" не является волшебной пулей и может стать самой проблемой производительности. Позаботьтесь о своем использовании и сценариях.
Ответ 3
A (очень неэлегантное) решение состоит в том, чтобы сохранить селектор как тип данных в объекте dataTransfer
. Вот пример: http://jsfiddle.net/TrevorBurnham/eKHap/
Активные строки здесь
e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');
Затем в событиях dragover
и dragenter
e.dataTransfer.types
содержит строку 'draggable'
, которая является идентификатором, необходимым для определения того, какой элемент перетаскивается. (Обратите внимание, что браузеру, по-видимому, требуется установить данные для распознанного типа MIME, например text/html
, чтобы это работало. Протестировано в Chrome и Firefox.)
Это уродливый, уродливый хак, и если кто-то может дать мне лучшее решение, я с радостью дам им щедрость.
Обновление:. Одно замечание, которое стоит добавить, заключается в том, что в дополнение к неэлегантности спецификация заявляет, что все типы данных будут преобразованы в нижний регистр ASCII. Поэтому будьте предупреждены, что селекторы с заглавными буквами или unicode сломаются. Решение Jeffery устраняет эту проблему.
Ответ 4
Учитывая текущую спецификацию, я не думаю, что есть какое-то решение, которое не является "взломом". Ходатайство WHATWG - один из способов устранить это: -)
Расширение на "(очень неэлегантное) решение" (демонстрация):
-
Создайте глобальный хэш всех элементов, которые в настоящее время перетаскиваются:
var dragging = {};
-
В обработчике dragstart
присвойте идентификатору перетаскивания элементу (если он еще не установлен), добавьте элемент в глобальный хэш, затем добавьте идентификатор перетаскивания в виде данных:
var dragId = this.dragId;
if (!dragId) {
dragId = this.dragId = (Math.random() + '').replace(/\D/g, '');
}
dragging[dragId] = this;
e.dataTransfer.setData('text/html', dragId);
e.dataTransfer.setData('dnd/' + dragId, dragId);
-
В обработчике dragenter
найдите идентификатор перетаскивания среди типов данных и извлеките исходный элемент из глобального хэша:
var types = e.dataTransfer.types, l = types.length, i = 0, match, el;
for ( ; i < l; i++) {
match = /^dnd\/(\w+)$/.exec(types[i].toLowerCase());
if (match) {
el = dragging[match[1]];
// do something with el
}
}
Если вы сохраняете хэш-код dragging
приватным для вашего собственного кода, сторонний код не сможет найти исходный элемент, даже если он может получить доступ к идентификатору перетаскивания.
Это предполагает, что каждый элемент можно перетаскивать только один раз; с multi-touch, я полагаю, можно было бы перетаскивать один и тот же элемент несколько раз, используя разные пальцы...
Обновление:. Чтобы разрешить несколько перетаскиваний в одном элементе, мы можем включить количество перетаскивания в глобальном хэше: http://jsfiddle.net/jefferyto/eKHap/2/
Ответ 5
Вы можете определить, что перетаскивается при перетаскивании, и сохранять его в переменной, которая будет использоваться при запуске событий dragover/dragenter:
var draggedElement = null;
function drag(event) {
draggedElement = event.srcElement || event.target;
};
function dragEnter(event) {
// use the dragged element here...
};
Ответ 6
В событии drag
скопируйте объект event.x
и event.y
в объект и установите его как значение свойства expando на перетаскиваемом элементе.
function drag(e) {
this.draggingAt = { x: e.x, y: e.y };
}
В событиях dragenter
и dragleave
найдите элемент, значение свойства expando которого соответствует event.x
и event.y
текущего события.
function dragEnter(e) {
var draggedElement = dragging.filter(function(el) {
return el.draggingAt.x == e.x && el.draggingAt.y == e.y;
})[0];
}
Чтобы уменьшить количество элементов, на которые нужно смотреть, вы можете отслеживать элементы, добавляя их в массив или назначая класс в событии dragstart
и уничтожая это в событии dragend
.
var dragging = [];
function dragStart(e) {
e.dataTransfer.setData('text/html', '');
dragging.push(this);
}
function dragEnd(e) {
dragging.splice(dragging.indexOf(this), 1);
}
http://jsfiddle.net/gilly3/4bVhL/
Теперь, теоретически это должно сработать. Однако я не знаю, как включить перетаскивание сенсорного устройства, поэтому я не смог его протестировать. Эта ссылка отформатирована в мобильном формате, но прикосновение и слайд не вызвали перетаскивания для запуска моего андроида. http://fiddle.jshell.net/gilly3/4bVhL/1/show/
Изменить: Из того, что я прочитал, не похоже, что перетаскивание HTML5 поддерживается на любых сенсорных устройствах. Можете ли вы перетаскивать работу на любых сенсорных устройствах? Если нет, multi-touch не будет проблемой, и вы можете прибегнуть к простому хранению перетаскиваемого элемента в переменной.
Ответ 7
Чтобы проверить, является ли это файлом, используйте:
e.originalEvent.dataTransfer.items[0].kind
Для проверки типа используйте:
e.originalEvent.dataTransfer.items[0].type
т.е. я хочу разрешить только один файл JPG, PNG, GIF, BMP
var items = e.originalEvent.dataTransfer.items;
var allowedTypes = ["image/jpg", "image/png", "image/gif", "image/bmp"];
if (items.length > 1 || items["0"].kind != "file" || items["0"].type == "" || allowedTypes.indexOf(items["0"].type) == -1) {
//Type not allowed
}
Ссылка: https://developer.mozilla.org/it/docs/Web/API/DataTransferItem
Ответ 8
Из того, что я прочитал в MDN, вы делаете правильно.
MDN перечисляет некоторые рекомендуемые типы перетаскивания, такие как text/html, но если они не подходят, просто сохраните идентификатор в виде текста, text/html 'или создать свой собственный тип, например' application/ node -id '.
Ответ 9
Я думаю, вы можете получить его, позвонив e.relatedTarget
См.: http://help.dottoro.com/ljogqtqm.php
ОК, я пробовал e.target.previousElementSibling
, и он работает, sorta.... http://jsfiddle.net/Gz8Qw/4/ Я думаю, что он зависает, потому что событие дважды выстрелил. Один раз для div и один раз для текста node (когда он запускается для текста node, он undefined
). Не уверен, что это приведет вас туда, где вы хотите быть или нет...