简体   繁体   English

拖放 API 停止在 Android Chrome 上使用自定义 MIME 类型

[英]Drag and Drop API stops working with custom MIME type on Android Chrome

Question: Are there known issues with Drag and Drop on Android Chrome or am I using the Drag and Drop API incorrectly?问题: Android Chrome 上的拖放是否存在已知问题,或者我是否错误地使用了拖放 API? (if neither, I'm going to assume it's a bug and submit a report to Chrome). (如果两者都不是,我将假设这是一个错误并向 Chrome 提交报告)。

Issue: I'm implementing a simple Drag and Drop interface that seems to be working fine, but is behaving strangely on 1 of 3 device/browser combinations that I'm testing.问题:我正在实现一个简单的拖放界面,该界面似乎工作正常,但在我正在测试的 3 个设备/浏览器组合中的 1 个上表现异常。 I've determined that the problem is in the specification of a custom MIME type 'text/custom' that I set on the 'dragstart' event of the draggable element, but nothing I'm reading in the Living Standard or on MDN is leading me to believe that this should be a problem.我已经确定问题出在我在draggable元素的'dragstart'事件上设置的自定义 MIME 类型'text/custom'的规范中,但我在 Living Standard 或 MDN 上阅读的内容都没有领先我相信这应该是一个问题。

It works fine on Chrome on my desktop ( Windows 10, Chrome 97.0.4692.71 ) and the iPhone 8 I'm testing ( iOS 15.0, Chrome 94.0.4606.76 ), but fails on Chrome on my Galaxy S20 ( Android 11, Chrome 96.0.4664.104 ). It works fine on Chrome on my desktop ( Windows 10, Chrome 97.0.4692.71 ) and the iPhone 8 I'm testing ( iOS 15.0, Chrome 94.0.4606.76 ), but fails on Chrome on my Galaxy S20 ( Android 11, Chrome 96.0.4664.104 )。

As soon as I revert the MIME type in draggable.setData(...) from 'text/custom' to 'text/plain' , all interfaces work as expected, but I'd like to have a custom MIME type if possible, and I haven't found anything online that would lead me to believe that I can't do that.一旦我将draggable.setData(...)中的 MIME 类型从'text/custom'恢复为'text/plain' ,所有界面都会按预期工作,但如果可能的话,我希望有一个自定义 MIME 类型,而且我还没有在网上找到任何能让我相信我做不到的东西。

Why do I care?: I want an understanding of what's happening most of all, but the reason for trying to use a custom MIME type is to bail out of the various 'drag...' handlers on container (with an e.dataTransfer.types.includes('text/custom') check) for any other draggable interactions that may incidentally cross paths with my interface (eg: dragging a file from the OS into the browser on my page or dragging a link/URL across container ).我为什么要关心?:我想了解最重要的是发生了什么,但尝试使用自定义 MIME 类型的原因是为了摆脱container上的各种'drag...'处理程序(使用e.dataTransfer.types.includes('text/custom')检查)是否有可能偶然与我的界面交叉路径的任何其他draggable交互(例如:将文件从操作系统拖动到我页面上的浏览器中或跨container拖动链接/URL) .

Code (stripped as much as possible for minimal demonstration of issue):代码(尽可能剥离以最小化问题演示):

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <style>
    #container {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 150px;
      width: 200px;
      background-color: lightblue;
    }

    #draggable-tester {
      display: flex;
      justify-content: center;
      align-items: center;
      margin-top: 20px;
      height: 40px;
      width: 120px;
      background-color: lightgreen;
    }
  </style>
</head>
<body>
  <div id="container">Container</div>
  <div id="draggable-tester" draggable="true">Draggable</div>

  <script>
    const draggableMimeType = 'text/custom';

    // Initialize draggable element.
    const draggable = document.getElementById('draggable-tester');
    draggable.addEventListener('dragstart', (e) => {
      e.dataTransfer.setData(draggableMimeType, 'data payload');
      console.log('dragstart event fired on draggable');
    });
    draggable.addEventListener('dragend', () => {
      console.log('dragend event fired on draggable');
    });

    // Initialize drag container and drop zone.
    const dragContainer = document.getElementById('container');
    dragContainer.addEventListener('dragenter', (e) => {
      e.preventDefault();
      console.log('dragenter event fired on container');
    });
    dragContainer.addEventListener('dragover', (e) => {
      e.preventDefault();
    });
    dragContainer.addEventListener('drop', (e) => {
      console.log(`drop w/ data: '${e.dataTransfer.getData(draggableMimeType)}'`);
    });
  </script>
</body>

Results: On desktop and iPhone, the above gives me the expected results as I execute the various drag actions on the drag container and drop the draggable element, but on Android it only logs the 'dragstart' message (it doesn't even log the 'dragend' registered on draggable ).结果:在桌面和 iPhone 上,当我在拖动容器上执行各种拖动操作并放下draggable元素时,上面给出了预期的结果,但在 Android 上它只记录'dragstart'消息(它甚至不记录'dragend'draggable上注册)。 As soon as I modify draggableMimeType to be 'text/plain' , it works on all devices as expected.一旦我将draggableMimeType修改为'text/plain' ,它就可以按预期在所有设备上运行。

I think it's a bug.我认为这是一个错误。 I've submitted an issue here: https://bugs.chromium.org/p/chromium/issues/detail?id=1293803我在这里提交了一个问题: https://bugs.chromium.org/p/chromium/issues/detail?id=1293803

I'm working around it in the meantime by sidestepping the Drag and Drop API's data provision mechanism altogether.与此同时,我正在通过完全回避拖放 API 的数据提供机制来解决这个问题。 Apparently you have to call setData() in the dragstart handler in order for the draggable interaction to work at all, but I never utilize the other half of that interaction with getData .显然,您必须在dragstart处理程序中调用setData()才能使draggable交互完全起作用,但我从不使用与getData交互的另一半。

Instead, I'm sharing a little bit of "global" state (it's only global across a few dozen lines inside of a module) across the various event handlers, with dragstart setting the global to house our element that is currently being dragged and all of the container drag... events looking at that global for indication as to whether the drag pertains to our custom draggable element or some other element from out in the wild (eg: files from other contexts/applications or links dragged through the interface (in which case it just bails out)).相反,我在各种事件处理程序中共享一点“全局”state(它仅在模块内的几十行中是全局的), dragstart设置全局以容纳我们当前正在拖动的元素和所有容器drag...事件查看该全局以指示拖动是否与我们的自定义draggable元素或来自野外的某些其他元素有关(例如:来自其他上下文/应用程序的文件或通过界面拖动的链接(在这种情况下,它只是退出))。 dragend is then responsible for setting the global to undefined so that we're appropriately maintaining our indication of whether a drag is supposed to be handled by our own logic.然后, dragend负责将全局设置为undefined ,以便我们适当地维护是否应该由我们自己的逻辑处理拖动的指示。

A small snippet:一个小片段:

// Establish a global for tracking the in-house element currently being dragged.
let draggingElement = undefined;

// Initialize draggable element.
const draggable = document.getElementById('draggable-tester');
draggable.addEventListener('dragstart', (e) => {
  e.dataTransfer.setData('text/plain', '');
  draggingElement = draggable;
  console.log('dragstart event fired on draggable');
});
draggable.addEventListener('dragend', () => {
  draggingElement = undefined;
  console.log('dragend event fired on draggable');
});

// Initialize drag container and drop zone.
const dragContainer = document.getElementById('container');
dragContainer.addEventListener('dragenter', (e) => {
  if (!draggingElement) return;

  e.preventDefault();
  console.log('dragenter event fired on container');
});
dragContainer.addEventListener('dragover', (e) => {
  if (!draggingElement) return;

  e.preventDefault();
});
dragContainer.addEventListener('drop', () => {
  if (!draggingElement) return;

  console.log(`dropping element w/ id: "${draggingElement.getAttribute('id')}"`);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM