【问题标题】:HTML5 DnD dataTransfer setData or getData not working in every browser except FirefoxHTML5 DnD dataTransfer setData 或 getData 不能在除 Firefox 之外的所有浏览器中工作
【发布时间】:2012-08-09 06:28:04
【问题描述】:

考虑一下JSFiddle。它在 Firefox (14.0.1) 中运行良好,但在 Windows (7) 和 OS X (10.8) 上的 Chrome (21.0.1180.75)、Safari (?) 和 Opera(12.01?) 中失败。据我所知,问题出在setData()getData() 对象上的getData() 方法上。这是来自 JSFiddle 的相关代码。

var dragStartHandler = function (e) {
    e.originalEvent.dataTransfer.effectAllowed = "move";
    e.originalEvent.dataTransfer.setData("text/plain", this.id);
};

var dragEnterHandler = function (e) {
    //  dataTransferValue is a global variable declared higher up.
    //  No, I don't want to hear about why global variables are evil,
    //  that's not my issue.
    dataTransferValue = e.originalEvent.dataTransfer.getData("text/plain");

    console.log(dataTransferValue);
};

据我所知,这应该可以正常工作,如果您在拖动项目时查看控制台,您会看到写出的 id,这意味着它可以很好地找到元素并获取它的 id 属性。问题是,是不设置数据还是不获取数据?

我非常感谢您的建议,因为经过一周的尝试和大约 200 多个版本,我开始失去理智。我所知道的是它曾经在 60 版左右可以工作,并且特定代码根本没有改变......

实际上,6X 和 124 之间的主要区别之一是我将事件绑定从 live() 更改为 on()。我不认为这是问题所在,但我发现 Chrome 在处理 DnD 时遇到了一些失败。 这已被揭穿。事件绑定方法对问题没有影响。

更新

我创建了一个新的JSFiddle,它完全去除了所有内容,只留下了事件绑定和处理程序。我使用 jQuery 1.7.2 和 1.8 对它进行了测试,同时使用了 on()live()。问题仍然存在,所以我降低了一个级别并删除了所有框架并使用纯 JavaScript。问题仍然仍然存在,因此根据我的测试,不是我的代码失败了。相反,Chrome、Safari 和 Opera 似乎都在实施 setData()getData() 不规范,或者只是由于某种原因而失败。如果我错了,请纠正我。

无论如何,如果您查看新的 JSFiddle,您应该能够复制该问题,只需在您拖动指定接受放置的元素时查看控制台。我已经用Chromium 开了一张票。最后我可能仍然做错了什么,但我现在根本不知道如何做 DnD。新的 JSFiddle 被尽可能地精简了......

【问题讨论】:

标签: javascript jquery html drag-and-drop


【解决方案1】:

好的,经过一番挖掘,我发现问题实际上不在于 Chrome、Safari 和 Opera。给出的结果是 Firefox 支持它,我不能说其他浏览器都失败了,因为我通常会接受 IE。

问题的真正原因是DnD specification itself。根据dragdragenterdragleavedragoverdragend 事件的规范,拖动数据存储模式为受保护模式。你问什么是保护模式?它是:

对于所有其他事件。拖动数据存储中的格式和种类 可以枚举表示拖动数据的项目列表,但 数据本身不可用,无法添加新数据。

这意味着,“您无权访问您设置的数据,即使在只读模式下也不行!自己去 f@。”。真的吗?想出这个的天才是谁?

现在,要绕过这个限制,您几乎没有选择,我只会概述我想出的两个。您的第一个是使用邪恶的全局变量并污染全局命名空间。您的第二个选择是使用 HTML5 localStorage API 来执行 DnD API 一开始就应该提供的完全相同的功能!

如果你沿着这条路线走,我有,你现在正在实现两个 HTML5 API,不是因为你想要,而是因为你不得不。现在我开始欣赏PPK's rant about the disaster HTML5 DnD API。

最重要的是,需要更改规范以允许访问存储的数据,即使它仅处于只读模式。在我的情况下,with this JSFiddle,我实际上使用dragenter 作为一种方式来查看放置区域并验证我是否应该允许放置发生。

在这种情况下,Mozilla 显然选择不完全遵守规范,这就是为什么我的 JSFiddle 可以正常工作的原因。碰巧这是我完全支持不支持完整规范的一次。

【讨论】:

  • +1,jsfiddle.net/crl/rguLw8z2/1 不能在 FF 以外的其他浏览器上运行,这太愚蠢了
  • 如果他们使数据可用,这将基本上违反 javascript 沙箱原则。所以我认为你不应该讽刺地称他们为天才,事实上他们找到了一种方法来启用此功能,同时保护用户数据。
  • @LorenShqipognja,虽然自从这篇文章以来我已经多年没有再次使用 DnD API,并且产生这篇文章的原始项目也已经消失,但我仍然坚持我的讽刺,因为规范是还是错了。你被赋予了读写数据的方法,但是你写的越多,你就不能总是读,尤其是在非常关键的时刻,根据需要强制产生不同的结果。如果您必须使用 LocalStorage、数据属性或其他东西而不是提供 [工作] 功能的 API 来“破解”您的方式,那么规范的整个数据部分是毫无意义的。但是,我离题了。
  • 我疯了,想弄清楚为什么我的数据传输没有设置好
  • 2018,仍在为此苦苦挣扎。有我们使用的新 API 吗?
【解决方案2】:

“受保护”位是有原因的....拖放可以跨越完全不同的窗口,他们不希望有人能够实现一个“监听器”DIV 来窃听拖过它的所有内容(并且可能通过 AJAX 将这些内容发送到 Elbonia 的某个间谍服务器)。只有 DROP 区域(更清楚地在用户的控制之下)获得完整的独家新闻。

烦人,但我明白为什么它可能被认为是必要的。

【讨论】:

  • 您是否有指向围绕此问题进行的 WHATWG 讨论的链接?我想更多地了解他们的推理。
  • 请引用您的来源
  • 但是dragend 会在被拖动的项目上触发,对吧?你不应该需要保护它自己,不是吗?
  • 他们不能使用久经考验的同源策略吗?即使跨不同的 windows 数据也应该可以在同一个域中访问。
  • 没有来源。我正在处理一个涉及拖放的问题,并且在阅读本文时只是想到了一个想法。很抱歉之前没有提到这一点。
【解决方案3】:
var dragStartHandler = function (e) {
    e.originalEvent.dataTransfer.effectAllowed = "move";
    e.originalEvent.dataTransfer.setData("text/plain", this.id);
};

问题在于“文本/纯文本”。 MSDN 文档中 setData 的标准规范只是“文本”(没有 /plain)。在我尝试过的任何版本中,Chrome 都接受 /plain,但 IE 不接受。

我在同一个问题上苦苦挣扎了几个星期,试图弄清楚为什么我的“drop”事件在 IE 中没有正确触发,而在 Chrome 中却触发了。这是因为dataTransfer数据没有正确加载。

【讨论】:

  • 我无法从 WPF 内的 WebBrowser 对象中的 HTML 拖动到 c#。这解决了它。但是,IE11 本身绝对允许“text/plain”的拖动。 WebBrowser 控件中的相同代码不好。我查找了对象上的设置,以及整个浏览器功能设置的繁琐,但只有这个有效。
  • 这是我在 Safari Webkit 中使用 Java 构建 GUI 时遇到的一个问题。很好的分析!。
【解决方案4】:

我知道你已经回答了这个问题,但这是一个有用的线程——我只是想在这里添加一个附录——如果你自己设置数据,你总是可以将数据添加到字段本身中(丑我知道),但它避免了重新创建功能:

例如,如果设置您自己的自定义数据:

  dataTransfer.setData('mycustom/whatever', 'data');

将数据附加为新的数据条目,然后迭代:

  dataTransfer.setData('mycustom/whatever/data/{a json encoded string even}');

查询:

// naive webkit only look at the datatransfer.types
if (dataTransfer.types.indexOf('mycustom/whatever') >= 0) {

    var dataTest = 'mycustom/whatever/data/';

    // loop through types and create a map
    for (var i in types) {

        if (types[i].substr(0, dataTest.length) == dataTest) {

            // shows:
            // {a json encoded string even}
            console.log('data:', types[i].substr(dataTest.length));

            return; // your custom handler
        }
    }
}

仅在 chrome 中测试

【讨论】:

  • 嗯,这绝对是一种有趣的方式。想知道它如何与其他浏览器一起扩展。无论如何,我认为 DnD 规范需要修复。
  • 需要大量修复。而且我担心我们到目前为止建立的所有解决方法都会在浏览器真正意识到他们实现 dnd 系统的糟糕程度以及规范中不应该遵循的内容时中断(比如当前的愚蠢问题)
  • 这也是我想出的唯一解决方案,它可以检查 dragover(或 dragenter)以查看放置目标是否允许基于正在拖动的数据进行放置。丑陋且违反直觉,但希望能够拒绝和接受某些拖动的内容。
【解决方案5】:

还有一点值得注意的是,如果您使用超时离开执行链,则 dataTransfer 对象将不再拥有您的数据。 例如

function dropEventHandler(event){
    var dt = event.dataTransfer.getData("text/plain"); // works
    var myEvent = event;

    setTimeout(function(){
       var dt = myEvent.dataTranfer.getData("text/plain"); // null
    }, 1);
}

【讨论】:

    【解决方案6】:

    我在下面的代码中遇到了同样的错误:

    event.originalEvent.dataTransfer.setData("text/plain", event.target.getAttribute('id'));

    我将代码更改为:

    event.originalEvent.dataTransfer.effectAllowed = "移动"; event.originalEvent.dataTransfer.setData("text", event.target.getAttribute('id'));

    它对我有用。

    【讨论】:

      【解决方案7】:

      我看到这篇文章是因为我对 Chrome 的 dataTransfer.setData()dataTransfer.getData() 函数有类似的体验。

      我的代码看起来像这样:

      HTML:
      <div ondragstart="drag(event)" ondrop="newDrop(event)"></div>
      
      JAVASCRIPT:
      function drag(ev) {
          ev.dataTransfer.setData("text", ev.target.id);
      }
      function newDrop(ev){
          var itemDragged = ev.dataTransfer.getData("text");
          var toDropTo = ev.target.id;
      }
      

      该代码在 Internet Explorer 中运行良好,但在 Chrome 中进行测试时,我无法使用 newDrop 函数中的 dataTransfer.getData() 函数获取在我的 dataTransfer 对象(在拖动函数中设置)中设置的值。我也无法从语句 ev.target.id 中获取 id 值。

      在网上搜索了一番后,我发现我应该使用事件参数currentTarget 属性而不是事件target 属性。更新后的代码如下所示:

      JAVASCRIPT:
      function drag(ev) {
          ev.dataTransfer.setData("text", ev.currentTarget.id);
      }
      function newDrop(ev){
          var itemDragged = ev.dataTransfer.getData("text");
          var toDropTo = ev.currentTarget.id;
      }
      

      通过这一更改,我能够在 chrome 和 Internet Explorer 中使用 dataTransfer.setData()dataTransfer.getData() 函数。我没有在其他任何地方测试过,我不确定为什么会这样。希望这会有所帮助,希望有人能给出解释。

      【讨论】:

        【解决方案8】:

        我正在使用 Firefox 进行网站测试。

        在我笔记本电脑上的 WAMP 中,像 OP 这样的代码可以正常工作。但是,当我将网站移至 HOSTMONSTER 时,它在那里不起作用。

        我按照 Joshua Espana 的回答解决了我的问题。

        失败:

        ev.dataTransfer.setData("text/plain", ev.target.id);
        

        工作:

         ev.dataTransfer.setData("text", ev.currentTarget.id);
        

        谢谢,约书亚!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-13
          • 2011-03-20
          • 2011-05-07
          • 2016-11-20
          • 1970-01-01
          • 1970-01-01
          • 2021-07-09
          • 2020-07-26
          相关资源
          最近更新 更多