Javascript 确定从 dragenter 和 dragover 事件中拖动的内容
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11065803/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine what is being dragged from dragenter & dragover events
提问by Trevor Burnham
I'm trying to use the HTML5 draggable API (though I realize it has its problems). So far, the only showstopper I've encountered is that I can't figure out a way to determine what is being dragged when a dragover
or dragenter
event fires:
我正在尝试使用 HTML5 可拖动 API(尽管我意识到它有问题)。到目前为止,我遇到的唯一阻碍是我无法找到一种方法来确定当 adragover
或dragenter
事件触发时正在拖动什么:
el.addEventListener('dragenter', function(e) {
// what is the draggable element?
});
I realize I could assume that it's the last element to fire a dragstart
event, but... multitouch. I've also tried using e.dataTransfer.setData
from the dragstart
to attach a unique identifier, but apparently that data is inaccessiblefrom dragover
/dragenter
:
我意识到我可以假设它是触发dragstart
事件的最后一个元素,但是......多点触控。我也尝试使用e.dataTransfer.setData
fromdragstart
来附加唯一标识符,但显然无法从dragover
/访问数据dragenter
:
This data will only be available once a drop occurs during the drop event.
此数据仅在掉落事件期间发生掉落时可用。
So, any ideas?
那么,有什么想法吗?
Update:As of this writing, HTML5 drag-and-drop does not appear to be implemented in any major mobile browser, making the point about multitouch moot in practice. However, I'd like a solution that's guaranteed to work across any implementation of the spec, which does not appear to preclude multiple elements from being dragged simultaneously.
更新:在撰写本文时,HTML5 拖放似乎尚未在任何主要移动浏览器中实现,这使得多点触控在实践中没有实际意义。但是,我想要一个解决方案,它可以保证在 spec 的任何实现中都有效,它似乎不会阻止同时拖动多个元素。
I've posted a working solutionbelow, but it's an ugly hack. I'm still hoping for a better answer.
我在下面发布了一个可行的解决方案,但这是一个丑陋的黑客。我仍然希望得到更好的答案。
采纳答案by Trevor Burnham
The short answer to my question turns out to be: No. The WHATWG specdoesn't provide a reference to the element being dragged (called the "source node" in the spec) in the dragenter
, dragover
, or dragleave
events.
简短的回答我的问题真可谓是:否。WHATWG规范不提供的一个参考要素被拖动(称为规范“源节点”) dragenter
,dragover
或dragleave
事件。
Why not? Two reasons:
为什么不?两个原因:
First, as Jeffery points out in his comment, the WHATWG spec is based on IE5+'s implementation of drag-and-drop, which predated multi-touch devices. (As of this writing, no major multi-touch browser implements HTML drag-and-drop.) In a "single-touch" context, it's easy to store a global reference to the current dragged element on dragstart
.
首先,正如 Jeffery 在他的评论中指出的那样,WHATWG 规范基于 IE5+ 的拖放实现,它早于多点触控设备。(在撰写本文时,没有主要的多点触控浏览器实现 HTML 拖放。)在“单点触控”上下文中,很容易在 上存储对当前拖动元素的全局引用dragstart
。
Second, HTML drag-and-drop allows you to drag elements across multiple documents. This is awesome, but it also means that providing a reference to the element being dragged in every dragenter
, dragover
, or dragleave
event wouldn't make sense; you can't reference an element in a different document. It's a strength of the API that those events work the same way whether the drag originated in the same document or a different one.
其次,HTML 拖放允许您跨多个文档拖动元素。这是真棒,但它也意味着提供给该元件为基准在每一个拖累dragenter
,dragover
或dragleave
事件就没有意义; 您不能引用不同文档中的元素。API 的一个优势在于,无论拖动源自同一个文档还是不同的文档,这些事件都以相同的方式工作。
But the inability to provide serialized information to all drag events, except through dataTransfer.types
(as described in my working solutionanswer), is a glaring omission in the API. I've submitted a proposal for public data in drag eventsto the WHATWG, and I hope you'll express your support.
但是,除了通过dataTransfer.types
(如我的工作解决方案答案中所述)之外,无法向所有拖动事件提供序列化信息是 API 中的一个明显遗漏。我已经向WHATWG提交了一份关于拖动事件中公共数据的提案,希望您能表达您的支持。
回答by bladnman
I wanted to add a very clear answer here so that it was obvious to everyone who wanders past here. It's been said several times in other answers, but here it is, as clear as I can make it:
我想在这里添加一个非常明确的答案,以便每个路过这里的人都能清楚地看到。在其他答案中已经说过好几次了,但在这里,我尽可能清楚:
dragover
DOES NOT HAVE THE RIGHTS to see the data in the drag event.
dragover
无权查看拖动事件中的数据。
This information is only available during the DRAG_START and DRAG_END (drop).
此信息仅在 DRAG_START 和 DRAG_END(丢弃)期间可用。
The issue is it's not obvious at all and maddening until you happen to read deeply enough on the spec or places like here.
问题是它根本不明显而且令人抓狂,直到您碰巧对规范或像这里这样的地方进行了足够深入的阅读。
WORK-AROUND:
解决方法:
As a possible work-around I have added special keys to the DataTransfer object and tested those. For example, to improve efficiency I wanted to look up some "drop target" rules when my drag started instead of every time a "drag over" occurred. To do this I added keys identifying each rule onto the dataTransfer object and tested those with "contains".
作为一种可能的解决方法,我向 DataTransfer 对象添加了特殊键并对其进行了测试。例如,为了提高效率,我想在拖动开始时查找一些“放置目标”规则,而不是每次发生“拖动”时。为此,我在 dataTransfer 对象上添加了标识每个规则的键,并用“包含”测试了那些。
ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders")
And things like that. To be clear, that "includes" is not a magic bullet and can become a performance concern itself. Take care to understand your usage and scenarios.
以及诸如此类的事情。需要明确的是,“包含”不是灵丹妙药,它本身可能会成为性能问题。请注意了解您的用法和场景。
回答by Trevor Burnham
A (very inelegant) solution is to store a selector as a typeof data in the dataTransfer
object. Here is an example: http://jsfiddle.net/TrevorBurnham/eKHap/
A(非常不雅)的解决方案是选择存储为类型中的数据的dataTransfer
对象。这是一个例子:http: //jsfiddle.net/TrevorBurnham/eKHap/
The active lines here are
这里的活动线是
e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');
Then in the dragover
and dragenter
events, e.dataTransfer.types
contains the string 'draggable'
, which is the ID needed to determine which element is being dragged. (Note that browsers apparently require data to be set for a recognized MIME type like text/html
as well in order for this to work. Tested in Chrome and Firefox.)
然后在dragover
anddragenter
事件中,e.dataTransfer.types
包含 string 'draggable'
,这是确定要拖动哪个元素所需的 ID。(请注意,浏览器显然也需要为可识别的 MIME 类型设置数据才能text/html
使其正常工作。在 Chrome 和 Firefox 中测试。)
It's an ugly, ugly hack, and if someone can give me a better solution, I'll happily grant them the bounty.
这是一个丑陋的、丑陋的黑客,如果有人能给我一个更好的解决方案,我会很乐意给他们赏金。
Update:One caveat worth adding is that, in addition to being inelegant, the spec states that all data types will be converted to lower-case ASCII. So be warned that selectors involving capital letters or unicode will break. Jeffery's solutionsidesteps this issue.
更新:值得补充的一个警告是,除了不优雅之外,规范还指出所有数据类型都将转换为小写 ASCII。所以请注意,涉及大写字母或 unicode 的选择器会损坏。Jeffery 的解决方案回避了这个问题。
回答by Jeffery To
Given the current spec, I don't think there is any solution that isn't a "hack". Petitioningthe WHATWG is one way to get this fixed :-)
鉴于当前的规范,我认为没有任何解决方案不是“黑客”。请愿WHATWG 是解决此问题的一种方法:-)
Expanding on the "(very inelegant) solution" (demo):
扩展“(非常不雅的)解决方案”(演示):
Create a global hash of all elements currently being dragged:
var dragging = {};
In the
dragstart
handler, assign a drag ID to the element (if it doesn't have one already), add the element to the global hash, then add the drag ID as a data type: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);
In the
dragenter
handler, find the drag ID among the data types and retrieve the original element from the global hash: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 } }
创建当前被拖动的所有元素的全局散列:
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 } }
If you keep the dragging
hash private to your own code, third-party code would not be able to find the original element, even though they can access the drag ID.
如果您dragging
对自己的代码保留哈希私有,则第三方代码将无法找到原始元素,即使他们可以访问拖动 ID。
This assumes that each element can only be dragged once; with multi-touch I suppose it would be possible to drag the same element multiple times using different fingers...
这假设每个元素只能被拖动一次;使用多点触控我想可以使用不同的手指多次拖动相同的元素......
Update:To allow for multiple drags on the same element, we can include a drag count in the global hash: http://jsfiddle.net/jefferyto/eKHap/2/
更新:为了允许在同一个元素上进行多次拖动,我们可以在全局哈希中包含一个拖动计数:http: //jsfiddle.net/jefferyto/eKHap/2/
回答by tjscience
You can determine what is being dragged when the drag starts and save this in a variable to use when the dragover/dragenter events are fired:
您可以确定拖动开始时正在拖动的内容,并将其保存在一个变量中,以便在触发 dragover/dragenter 事件时使用:
var draggedElement = null;
function drag(event) {
draggedElement = event.srcElement || event.target;
};
function dragEnter(event) {
// use the dragged element here...
};
回答by user2272143
To check if it is a file use:
要检查它是否是文件,请使用:
e.originalEvent.dataTransfer.items[0].kind
To check the type use:
要检查类型使用:
e.originalEvent.dataTransfer.items[0].type
i.e. I want to allow only one single file jpg, png, gif, bmp
即我只想允许一个单一的文件 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
}
Reference: https://developer.mozilla.org/it/docs/Web/API/DataTransferItem
参考:https: //developer.mozilla.org/it/docs/Web/API/DataTransferItem
回答by gilly3
In the drag
event, copy event.x
and event.y
to an object and set it as the value of an expando property on the dragged element.
在该drag
事件中,将event.x
和复制event.y
到一个对象并将其设置为拖动元素上的 expando 属性的值。
function drag(e) {
this.draggingAt = { x: e.x, y: e.y };
}
In the dragenter
and dragleave
events find the element whose expando property value matches the event.x
and event.y
of the current event.
在dragenter
anddragleave
事件中查找其 expando 属性值与当前事件的event.x
and匹配的元素event.y
。
function dragEnter(e) {
var draggedElement = dragging.filter(function(el) {
return el.draggingAt.x == e.x && el.draggingAt.y == e.y;
})[0];
}
To reduce the number of elements you need to look at, you can keep track of elements by adding them to an array or assigning a class in the dragstart
event, and undoing that in the dragend
event.
为了减少您需要查看的元素数量,您可以通过将元素添加到数组或在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://jsfiddle.net/gilly3/4bVhL/
Now, in theory this should work. However, I don't know how to enable dragging for a touch device, so I wasn't able to test it. This link is mobile formatted, but touch and slide didn't cause dragging to start on my android. http://fiddle.jshell.net/gilly3/4bVhL/1/show/
现在,理论上这应该有效。但是,我不知道如何为触摸设备启用拖动,因此我无法对其进行测试。此链接是移动格式的,但触摸和滑动不会导致在我的 android 上开始拖动。 http://fiddle.jshell.net/gilly3/4bVhL/1/show/
Edit:From what I've read, it doesn't look like HTML5 draggable is supported on any touch devices. Are you able to get draggable working on any touch devices? If not, multi-touch wouldn't be an issue and you can resort to just storing the dragged element in a variable.
编辑:据我所知,任何触摸设备都不支持 HTML5 可拖动。您是否能够在任何触摸设备上进行可拖动工作?如果没有,多点触控就不是问题,您可以将拖动的元素存储在变量中。
回答by Jules
From what I have read on MDN, what you are doing is correct.
从我在 MDN 上阅读的内容来看,您所做的是正确的。
MDN lists some recommended drag types, such as text/html, but if none are suitable then just store the id as text using the 'text/html' type, or create your own type, such as 'application/node-id'.
MDN 列出了一些推荐的拖动类型,例如 text/html,但如果没有合适的,那么只需使用 'text/html' 类型将 id 存储为文本,或者创建您自己的类型,例如 'application/node-id'。
回答by solidau
I think you can get it by calling e.relatedTarget
See: http://help.dottoro.com/ljogqtqm.php
我想你可以通过调用来获取它e.relatedTarget
参见:http: //help.dottoro.com/ljogqtqm.php
OK, I tried e.target.previousElementSibling
and it works, sorta.... http://jsfiddle.net/Gz8Qw/4/I think it hangs up because the event is being fired twice. Once for the div and once for the text node (when it fires for text node, it is undefined
). Not sure if that will get you where you want to be or not...
好的,我试过了e.target.previousElementSibling
,它有效,有点...... http://jsfiddle.net/Gz8Qw/4/我认为它挂断了,因为该事件被触发了两次。一次用于 div,一次用于文本节点(当它为文本节点触发时,它是undefined
)。不知道这是否会让你到达你想去的地方......