【问题标题】:Drag and Drop API stops working with custom MIME type on Android Chrome拖放 API 停止在 Android Chrome 上使用自定义 MIME 类型
【发布时间】:2022-01-12 19:56:29
【问题描述】:

问题: Android Chrome 上的拖放是否存在已知问题,或者我是否错误地使用了拖放 API? (如果两者都没有,我会假设这是一个错误并向 Chrome 提交报告)。

问题:我正在实现一个简单的拖放界面,该界面似乎工作正常,但在我正在测试的 3 个设备/浏览器组合中的 1 个上表现异常。我已经确定问题出在我在 draggable 元素的 'dragstart' 事件上设置的自定义 MIME 类型 'text/custom' 的规范中,但我在 Living Standard 或 MDN 上阅读的内容都没有领先我相信这应该是个问题。

它在我桌面上的 Chrome (Windows 10, Chrome 97.0.4692.71) 和我正在测试的 iPhone 8 (iOS 15.0, Chrome 94.0.4606.76) 上运行良好,但在我的 Galaxy S20 (Android 11, Chrome 96.0.4664.104) 上的 Chrome 上运行失败。

只要我将 draggable.setData(...) 中的 MIME 类型从 'text/custom' 恢复为 'text/plain',所有接口都会按预期工作,但如果可能的话,我希望有一个自定义 MIME 类型,但我还没有找到网上任何会让我相信我做不到的事情。

我为什么要关心?:我最想了解正在发生的事情,但尝试使用自定义 MIME 类型的原因是为了摆脱各种 'drag...' 处理程序container(带有e.dataTransfer.types.includes('text/custom') 检查)用于可能偶然与我的界面交叉路径的任何其他draggable 交互(例如:将文件从操作系统拖到我页面上的浏览器中或将链接/URL 拖到@987654334 @)。

代码(尽可能删除以尽量减少问题演示):

<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>

结果:在桌面和 iPhone 上,当我在拖动容器上执行各种拖动操作并放下 draggable 元素时,上面给出了预期的结果,但在 Android 上它只记录'dragstart' 消息(它甚至不记录在draggable 上注册的'dragend')。只要我将draggableMimeType 修改为'text/plain',它就可以按预期在所有设备上运行。

【问题讨论】:

    标签: javascript android html google-chrome drag-and-drop


    【解决方案1】:

    我认为这是一个错误。我在这里提交了一个问题:https://bugs.chromium.org/p/chromium/issues/detail?id=1293803

    与此同时,我正在通过完全回避拖放 API 的数据提供机制来解决这个问题。显然,您必须在 dragstart 处理程序中调用 setData() 才能使 draggable 交互工作,但我从未利用与 getData 交互的另一半。

    相反,我在各种事件处理程序之间共享一点“全局”状态(它只是在模块内的几十行中是全局的),dragstart 将全局设置为容纳我们的元素当前正在被拖动,所有容器drag... 事件都在查看该全局以指示拖动是否与我们的自定义draggable 元素或来自野外的一些其他元素有关(例如:来自其他上下文/应用程序的文件或通过界面拖动的链接(在这种情况下它只是退出)。然后dragend 负责将全局设置为undefined,以便我们适当地维护是否应该由我们自己的逻辑处理拖动的指示。

    一个小sn-p:

    // 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')}"`);
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-23
      • 2011-08-19
      • 1970-01-01
      • 2013-08-10
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      相关资源
      最近更新 更多