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

Tre*_*ham 64 javascript html5 draggable

我正在尝试使用HTML5可拖动的API(虽然我意识到它有它的问题).到目前为止,我遇到的唯一一个显示是我无法找到一种方法来确定当一个dragoverdragenter事件触发时被拖动的内容:

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

我意识到我可以认为它是发射dragstart事件的最后一个元素,但是......多点触控.我也试着使用e.dataTransfer.setDatadragstart附加一个唯一的标识符,但显然这些数据是不可访问dragover/ dragenter:

只有在丢弃事件期间发生丢弃时,才能使用此数据.

那么,有什么想法吗?

更新:在撰写本文时,HTML5拖放似乎没有在任何主流移动浏览器中实现,因此在实践中提出了关于多点触控的观点.但是,我想要一个可以保证在规范的任何实现中都能使用的解决方案,这似乎不会阻止同时拖动多个元素.

我在下面发布了一个有效的解决方案,但这是一个丑陋的黑客攻击.我仍然希望有更好的答案.

bla*_*man 57

我想在这里添加一个非常明确的答案,这样对于那些在这里徘徊的人来说显而易见.在其他答案中已多次说过,但在这里,我可以清楚地说出来:

dragover 没有权利在拖动事件中查看数据.

此信息仅在DRAG_START和DRAG_END(丢弃)期间可用.

问题是它根本不明显并且令人抓狂,直到你碰巧在这里的规格或地点上读得太深.

解决方法:

作为一种可能的解决方法,我已经为DataTransfer对象添加了特殊键并对其进行了测试.例如,为了提高效率,我想在我的拖动开始时查找一些"放下目标"规则,而不是每次发生"拖拽"时.为此,我添加了将每个规则标识到dataTransfer对象上的密钥,并使用"contains"对其进行了测试.

ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders")
Run Code Online (Sandbox Code Playgroud)

这样的事情.需要明确的是,"包含"并不是一个神奇的子弹,可以成为一个性能问题本身.注意了解您的使用情况和方案.

  • 我喜欢清晰的答案在我的脸上大喊。容易记住的:D (3认同)

Tre*_*ham 21

简短的回答我的问题真可谓是:否.WHATWG规范不提供在被拖动的元素(称为规范"源节点")的引用dragenter,dragoverdragleave事件.

为什么不?两个原因:

首先,正如Jeffery在评论中指出的那样,WHATWG规范基于IE5 +的拖放实现,这种实现早于多点触控设备.(在撰写本文时,没有主要的多点触控浏览器实现HTML拖放.)在"单触"上下文中,可以轻松地存储对当前拖动元素的全局引用dragstart.

其次,HTML拖放允许您跨多个文档拖动元素.这是真棒,但它也意味着提供对元素的引用被拖动每一个dragenter,dragoverdragleave事件就没有意义; 您不能在不同的文档中引用元素.无论拖动源自同一文档还是不同文档,这些事件的工作方式都是API的优势.

但是无法向所有拖动事件提供序列化信息,除非通过dataTransfer.types(如我的工作解决方案答案中所述),在API中是一个明显的遗漏.我已经 WHATWG 提交了拖拽事件中的公共数据提案,我希望你能表达你的支持.


Tre*_*ham 7

(非常不优雅)的解决方案是将选择器存储为对象中的一种数据dataTransfer.这是一个例子:http://jsfiddle.net/TrevorBurnham/eKHap/

这里的活动线是

e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');
Run Code Online (Sandbox Code Playgroud)

然后在dragoverdragenter事件中,e.dataTransfer.types包含字符串'draggable',这是确定拖动哪个元素所需的ID.(请注意,浏览器显然需要为识别的MIME类型设置数据text/html,以便使其正常工作.在Chrome和Firefox中测试.)

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

更新:值得补充的一点值得注意的是,除了不优雅之外,规范还规定所有数据类型都将转换为小写ASCII.因此请注意,涉及大写字母或unicode的选择器将会中断.Jeffery的解决方案回避了这个问题.


Jef*_* To 6

鉴于当前的规范,我认为没有任何解决方案不是“黑客”。请愿WHATWG 是解决此问题的一种方法:-)

扩展“(非常不雅的)解决方案”(演示):

  • 创建当前被拖动的所有元素的全局散列:

    var dragging = {};
    
    Run Code Online (Sandbox Code Playgroud)
  • 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);
    
    Run Code Online (Sandbox Code Playgroud)
  • 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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

如果您dragging对自己的代码保留哈希私有,则第三方代码将无法找到原始元素,即使他们可以访问拖动 ID。

这假设每个元素只能被拖动一次;使用多点触控我想可以使用不同的手指多次拖动相同的元素......


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


use*_*143 5

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

e.originalEvent.dataTransfer.items[0].kind
Run Code Online (Sandbox Code Playgroud)

要检查类型,请使用:

e.originalEvent.dataTransfer.items[0].type
Run Code Online (Sandbox Code Playgroud)

即我只想允许一个文件 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
}
Run Code Online (Sandbox Code Playgroud)

参考: https: //developer.mozilla.org/it/docs/Web/API/DataTransferItem