我正在尝试使用HTML5可拖动的API(虽然我意识到它has its problems)。到目前为止,我遇到的唯一一个问题是,当dragover
或dragenter
事件触发时,我无法找到确定被拖动内容的方法:
el.addEventListener('dragenter', function(e) {
// what is the draggable element?
});
我意识到我可以认为这是发起dragstart
事件的最后一个元素,但是...多点触控。我还尝试使用e.dataTransfer.setData
中的dragstart
附加唯一标识符,但显然该数据来自dragover
/ dragenter
{/ 3}}:
只有在丢弃事件发生丢弃时才能使用此数据。
那么,有什么想法吗?
更新:在撰写本文时,HTML5拖放似乎没有在任何主流移动浏览器中实现,因此在实践中提出了关于多点触控的观点。但是,我希望有一个解决方案可以保证适用于inaccessible的任何实现,这似乎不会阻止同时拖动多个元素。
我在下方发布了the spec,但这是一个丑陋的黑客攻击。我仍然希望有更好的答案。
答案 0 :(得分:50)
我想在这里添加一个非常明确的答案,以便每个在这里徘徊的人都很明显。在其他答案中已多次说过,但在这里,我可以清楚地说出来:
dragover
没有权利在拖动事件中查看数据。此信息仅在DRAG_START和DRAG_END(丢弃)期间可用。
问题是,在你碰巧仔细阅读这些规格或地点之前,它并不是很明显并且令人抓狂。
解决方法:
作为一种可能的解决方法,我已经为DataTransfer对象添加了特殊键并对其进行了测试。例如,为了提高效率,我想在我的拖动开始时查找一些“放下目标”规则,而不是每次发生“拖拽”时。为此,我添加了将每个规则标识到dataTransfer对象上的密钥,并使用“contains”对其进行了测试。
ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders")
这样的事情。要明确的是,“包含”并不是一个神奇的子弹,它本身可以成为一个性能问题。注意了解您的使用情况和方案。
答案 1 :(得分:20)
对我的问题的简短回答结果是:不。WHATWG spec没有提供{{1}中被拖动元素(在规范中称为“源节点”)的引用},dragenter
或dragover
个事件。
为什么不呢?有两个原因:
首先,正如Jeffery在his comment中指出的那样,WHATWG规范是基于IE5 +的拖放实现,这种实现早于多点触控设备。 (截至撰写本文时,没有主要的多点触控浏览器实现HTML拖放。)在“单触”上下文中,可以轻松地在dragleave
上存储对当前拖动元素的全局引用。 / p>
其次,HTML拖放允许您跨多个文档拖动元素。这很棒,但这也意味着在每个dragstart
,dragenter
或dragover
事件中提供对被拖动元素的引用都没有意义;您不能在不同的文档中引用元素。无论是否来自同一文档或不同文档,这些事件的工作方式都相同,这是API的优势。
但无法向所有拖拽事件提供序列化信息,除了通过dragleave
(我的working solution答案中所述),这是API中的一个明显遗漏。 我已经submitted a proposal for public data in drag events加入了WHATWG,我希望你能表达你的支持。
答案 2 :(得分:5)
(非常不优雅)的解决方案是将选择器存储为dataTransfer
对象中的类型数据。以下是一个示例:http://jsfiddle.net/TrevorBurnham/eKHap/
此处的有效行
e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');
然后在dragover
和dragenter
事件中,e.dataTransfer.types
包含字符串'draggable'
,这是确定拖动哪个元素所需的ID。 (请注意,浏览器显然需要为text/html
等已识别的MIME类型设置数据才能实现此目的。在Chrome和Firefox中进行测试。)
这是一个丑陋,丑陋的黑客,如果有人能给我一个更好的解决方案,我会高兴地给予他们赏金。
更新:值得补充的一点是,除了不优雅之外,规范还规定所有数据类型都将转换为小写ASCII。因此请注意,涉及大写字母或unicode的选择器将会中断。 Jeffery's solution回避了这个问题。
答案 3 :(得分:4)
鉴于目前的规范,我认为没有任何解决方案不是“黑客”。 Petitioning WHATWG是解决这个问题的一种方法: - )
扩展“(非常不优雅的)解决方案”(demo):
创建当前被拖动的所有元素的全局哈希:
var dragging = {};
在dragstart
处理程序中,为元素指定一个拖动ID(如果它还没有),将元素添加到全局哈希,然后将拖动ID添加为数据类型:
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
处理程序中,找到数据类型中的拖动ID并从全局哈希中检索原始元素:
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
哈希保密为您自己的代码,则第三方代码将无法找到原始元素,即使他们可以访问该拖动ID。
这假设每个元素只能拖动一次;使用多点触控我想可以使用不同的手指多次拖动相同的元素......
更新:要在同一元素上允许多次拖动,我们可以在全局哈希中包含拖动计数:http://jsfiddle.net/jefferyto/eKHap/2/
答案 4 :(得分:3)
您可以确定拖动开始时拖动的内容,并将其保存在变量中,以便在触发dragover / dragenter事件时使用:
var draggedElement = null;
function drag(event) {
draggedElement = event.srcElement || event.target;
};
function dragEnter(event) {
// use the dragged element here...
};
答案 5 :(得分:3)
在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可拖动。您是否能够在任何触摸设备上进行拖动?如果没有,多点触摸不会成为问题,您可以将拖动的元素存储在变量中。
答案 6 :(得分:1)
要检查它是否是文件用途:
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
答案 7 :(得分:0)
从我在MDN上看到的内容来看,你所做的是正确的。
MDN列出了一些recommended drag types,例如text / html,但如果没有合适,那么只需使用'text / html'类型将id存储为文本,或者创建自己的类型,例如'application /节点ID”。
答案 8 :(得分:-1)
我认为你可以通过致电 e.relatedTarget
来获取它。请参阅:http://help.dottoro.com/ljogqtqm.php
好的,我尝试了e.target.previousElementSibling
并且它有效,排序.... http://jsfiddle.net/Gz8Qw/4/我认为它会挂起,因为事件被解雇了两次。一次为div,一次为文本节点(当它为文本节点触发时,它为undefined
)。不确定这是否会让你到达你想去的地方......