簡體   English   中英

拖放 API 停止在 Android Chrome 上使用自定義 MIME 類型

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

問題: Android Chrome 上的拖放是否存在已知問題,或者我是否錯誤地使用了拖放 API? (如果兩者都不是,我將假設這是一個錯誤並向 Chrome 提交報告)。

問題:我正在實現一個簡單的拖放界面,該界面似乎工作正常,但在我正在測試的 3 個設備/瀏覽器組合中的 1 個上表現異常。 我已經確定問題出在我在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 )。

一旦我將draggable.setData(...)中的 MIME 類型從'text/custom'恢復為'text/plain' ,所有界面都會按預期工作,但如果可能的話,我希望有一個自定義 MIME 類型,而且我還沒有在網上找到任何能讓我相信我做不到的東西。

我為什么要關心?:我想了解最重要的是發生了什么,但嘗試使用自定義 MIME 類型的原因是為了擺脫container上的各種'drag...'處理程序(使用e.dataTransfer.types.includes('text/custom')檢查)是否有可能偶然與我的界面交叉路徑的任何其他draggable交互(例如:將文件從操作系統拖動到我頁面上的瀏覽器中或跨container拖動鏈接/URL) .

代碼(盡可能剝離以最小化問題演示):

<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'消息(它甚至不記錄'dragend'draggable上注冊)。 一旦我將draggableMimeType修改為'text/plain' ,它就可以按預期在所有設備上運行。

我認為這是一個錯誤。 我在這里提交了一個問題: https://bugs.chromium.org/p/chromium/issues/detail?id=1293803

與此同時,我正在通過完全回避拖放 API 的數據提供機制來解決這個問題。 顯然,您必須在dragstart處理程序中調用setData()才能使draggable交互完全起作用,但我從不使用與getData交互的另一半。

相反,我在各種事件處理程序中共享一點“全局”state(它僅在模塊內的幾十行中是全局的), dragstart設置全局以容納我們當前正在拖動的元素和所有容器drag...事件查看該全局以指示拖動是否與我們的自定義draggable元素或來自野外的某些其他元素有關(例如:來自其他上下文/應用程序的文件或通過界面拖動的鏈接(在這種情況下,它只是退出))。 然后, dragend負責將全局設置為undefined ,以便我們適當地維護是否應該由我們自己的邏輯處理拖動的指示。

一個小片段:

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