Подтвердить что ты не робот

Определите, что перетаскивается из событий 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, похоже, не реализовано ни в одном крупном мобильном браузере, что на практике указывает на мультитач. Тем не менее, я хотел бы, чтобы решение, которое гарантировало работу над любой реализацией спецификации, которая, как представляется, не позволяет одновременно перетаскивать несколько элементов.

Я разместил рабочее решение ниже, но это уродливое взломать. Я все еще надеюсь на лучший ответ.

4b9b3361

Ответ 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). Не уверен, что это приведет вас туда, где вы хотите быть или нет...