简体   繁体   中英

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? (if neither, I'm going to assume it's a bug and submit a report to 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. 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.

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.

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

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 ). As soon as I modify draggableMimeType to be 'text/plain' , it works on all devices as expected.

I think it's a bug. I've submitted an issue here: 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. 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 .

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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