确定从dragenter和dragover事件中拖出的内容

我试图使用HTML5可拖动的API(虽然我意识到它有问题 )。 到目前为止,我遇到的唯一不足就是我无法find一个方法来确定当dragoverdragenter事件触发时被拖动的东西:

 el.addEventListener('dragenter', function(e) { // what is the draggable element? }); 

我意识到我可以认为这是dragstart事件的最后一个元素,但是…多点触控。 我也尝试使用dragstart e.dataTransfer.setData来附加一个唯一的标识符,但显然这些数据是从dragover / dragenter 无法访问的 :

这个数据只有在下降事件发生时才会出现。

那么,有什么想法?

更新:在撰写本文时,HTML5的拖放似乎并没有在任何主stream的移动浏览器中实现,从而在实践中提出了多点触摸模拟的观点。 然而,我想要一个解决scheme,保证在任何规范的实现工作,这似乎并没有排除多个元素被同时拖动。

我已经发布了一个工作解决scheme ,但这是一个丑陋的黑客。 我仍然希望有更好的答案。

我想在这里添加一个非常明确的答案,以便在这里徘徊的每个人都很明显。 在其他答案中已经有好几次这样说了,但是在这里我清楚地知道:

 DRAG_OVER DOES NOT HAVE THE RIGHTS to see the data in the drag event. 

此信息仅在DRAG_START和DRAG_END(放置)期间可用。

这个问题是不是很明显,直到你碰巧阅读了这里的规范或地方。

解决方法:

作为一种可能的解决方法,我为DataTransfer对象添加了特殊键并对其进行了testing。 例如,为了提高效率,我想在我的拖动开始时查找一些“拖放目标”规则,而不是每次发生“拖动”。 为此,我将标识每个规则的关键字添加到dataTransfer对象中,并使用“contains”对其进行testing。

 ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders") 

像这样的事情 要清楚的是,“包括”不是一个神奇的子弹,可以成为一个性能问题本身。 注意了解您的使用情况和情况。

我的问题的简短答案结果是:不。WHATWG规范没有提供dragenterdragoverdragleave事件中被拖动的元素(在spec中称为“源节点”)的dragleave

为什么不? 两个原因:

首先,正如Jeffery在他的评论中指出的那样,WHATWG规范是基于IE5 +的拖放实现,这种devise早于多点触摸设备。 (截至本文撰写时,没有一款主要的多点触摸浏览器实现HTML拖放function)。在“单点触控”环境中,很容易将全局引用存储到dragstart上的当前拖动元素。

其次,HTML拖放function允许您将元素拖放到多个文档中。 这太棒了,但是这也意味着提供一个被牵引在每个dragenterdragoverdragleave事件中的元素的参考是没有意义的。 您不能引用不同文档中的元素。 这些API的强大之处在于,这些事件是以相同的方式工作,不pipe这些拖动来自于同一个文档还是不同的文档。

但是,除了通过dataTransfer.types (正如我的解决scheme的答案中所述),无法为所有拖动事件提供序列化的信息,这是API中的一个明显的漏洞。 我已经向 WHATWG 提交了一个拖放事件的公共数据提案,希望你能表示支持。

drag事件中,将event.xevent.y复制到对象,并将其设置为拖动元素上的expando属性的值。

 function drag(e) { this.draggingAt = { x: ex, y: ey }; } 

dragenterdragleave事件中find其expando属性值与当前事件的event.xevent.y匹配的event.x

 function dragEnter(e) { var draggedElement = dragging.filter(function(el) { return el.draggingAt.x == ex && el.draggingAt.y == ey; })[0]; } 

为了减less需要查看的元素的数量,您可以通过将元素添加到数组或在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/

现在,理论上这应该起作用。 但是,我不知道如何拖动触摸设备,所以我无法testing它。 这个链接是手机格式化,但触摸和幻灯片并没有导致拖动开始在我的android。 http://fiddle.jshell.net/gilly3/4bVhL/1/show/

编辑:从我读过的,它不像任何触摸设备支持HTML5可拖动。 你能够在任何触摸设备上得到可拖动的工作吗? 如果不是,多点触摸不会是一个问题,你可以求助于将拖动的元素存储在一个variables。

(非常不雅观)的解决scheme是将select器作为dataTransfer对象中的一种数据types存储。 这里是一个例子: http : //jsfiddle.net/TrevorBurnham/eKHap/

这里的活动线是

 e.dataTransfer.setData('text/html', 'foo'); e.dataTransfer.setData('draggable', ''); 

然后在dragoverdragenter事件中, e.dataTransfer.types包含string'draggable' ,这是确定哪个元素被拖动所需的ID。 (请注意,浏览器显然需要为text/html等公认的MIMEtypes设置数据,以便在Chrome和Firefox中testing)。

这是一个丑陋,丑陋的黑客,如果有人能给我一个更好的解决办法,我会高兴地给予他们的赏金。

更新:值得一提的是,除了不雅之外,规范还规定所有数据types都将被转换为小写ASCII。 所以要警告的是,包含大写字母或unicode的select器将被打破。 杰弗瑞的解决scheme回避了这个问题。

鉴于目前的规范,我不认为有任何解决scheme不是“黑客”。 请求 WHATWG是得到这个固定的一个方法:-)

扩大“(非常不雅)解决scheme”( 演示 ):

  • 创build当前被拖动的所有元素的全局哈希值:

     var dragging = {}; 
  • dragstart处理程序中,为元素指定一个拖动ID(如果它没有),将该元素添加到全局哈希,然后将该拖动ID添加为数据types:

     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处理程序中,在数据types中find拖动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哈希保留为自己的代码,则第三方代码将无法find原始元素,即使他们可以访问拖动ID。

这假定每个元素只能被拖动一次; 与多点触摸,我想可能会拖动相同的元素多次使用不同的手指…


更新:为了在同一个元素上允许多个拖动,我们可以在全局哈希中包含一个拖动计数: http : //jsfiddle.net/jefferyto/eKHap/2/

您可以确定拖动开始时正在拖动的内容,并将其保存在一个variables中,以便在dragover / dragenter事件触发时使用:

 var draggedElement = null; function drag(event) { draggedElement = event.srcElement || event.target; }; function dragEnter(event) { // use the dragged element here... }; 

要检查它是否是一个文件使用:

 e.originalEvent.dataTransfer.items[0].kind 

要检查types的使用:

 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

从我在MDN上看到的,你所做的是正确的。

MDN列出了一些推荐的拖拽types ,比如text / html,但是如果没有合适的话,只需要使用'text / html'types将id保存为文本,或者创build自己的types,比如'application / node-id'。

我认为你可以通过调用e.relatedTarget来获取它: http : e.relatedTarget

好吧,我试过e.target.previousElementSibling ,它的工作,sorta ….我认为它挂断了,因为事件正在被解雇两次。 一次为div,一次为文本节点(当它触发文本节点时,它是undefined )。 不知道这是否会让你想要成为或不是…