1

I have a handler function for the drop event which looks somehow like this:

da.addEventListener("drop", function(e) {
    e.stopPropagation();
    var xhr = new XMLHttpRequest();
    ... (configure XMLHttpRequest) 
    var fd = new FormData();
    [...e.dataTransfer.items].forEach(it => {
      it.getAsString(value => {
        console.log(`append ${it.type} ${value}`);
        fd.append(`${it.type}`, value);
      });
    });
    console.log("send");
    xhr.send(fd);
});

The problem here is that send() happens before fd.append(), because it.getAsString() seems to be asynchronous.

After reading some articles about callbacks, asynchronous functions, promises etc. I tried to do the actual appending in a .then() block:

it.getAsString(value => {
  return value;
}).then(value => {
  console.log(`append ${it.type} ${value}`);
  fd.append(`${it.type}`, value);
});

I expected to get into trouble with value not being propagated properly but instead I got

Uncaught TypeError: it.getAsString(...) is undefined

And even the integrated debugger underlines getAsString.

What am I doing here? Why seems getAsString to be defined when I use it without appending .then(value => {}) but works properly without .then()?

On a more abstract level - how would I correctly add items from a DataTransferItem to a FormData instance before sending it?

Update

Combining iterating through .items and then using .getData() works for me but it doesn't feel right..

[...e.dataTransfer.items].forEach(it => {
  fd.append(`${it.type}`, e.dataTransfer.getData(it.type));
});
frans
  • 8,868
  • 11
  • 58
  • 132
  • 1
    What items is your drag action transferring? I notice your code isn't testing for the [`kind`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/kind), so you might not want to use `getAsString()` in the first place – Bergi Mar 14 '22 at 10:44
  • Currently I'm just learning how to deal with drag&drop actions I haven't dealt with `kind` yet (and as you might see from my question I'm not very deep into JS yet, too). By now the drag action just transfers strings. But the code is also just a snipped stripped down to what I believe is important for the question. – frans Mar 14 '22 at 10:58
  • If you want to transfer one string per drag'n'drop, then use [`setData`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setData) and [`getData`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/getData). – Bergi Mar 14 '22 at 11:02
  • The thing is I'd like to transfer an arbitrary number of strings and I thought iterating through all attached `.items` would be the way to go... – frans Mar 14 '22 at 11:09
  • Where do you get an arbitrary number of strings from? What is your use case? – Bergi Mar 14 '22 at 11:18
  • I want to send data provided by the `dragable` element (e.g. a command together with arguments) together with the drop events coordinates to a server. Maybe sending just one element (e.g. a JSON string) is the more idiomatic approach, but as I said currently I want to find out how things work. Since `dataTransfer.items` _can_ store more than one string item I'd like to know how I should access them in a general way, or why I shouldn't _want_ to do it in the first place. – frans Mar 14 '22 at 11:30
  • A command with arguments should be a single element. You'd use multiple items if you had previously selected multiple items, e.g. in a file browser or multiselect dropdown. – Bergi Mar 14 '22 at 11:36
  • I believe you and I'll put the data in one single element. But that still doesn't answer my question: how would I synchronize with `getAsString()` and what does the error message really tell me? How would I get all data from `dataTransfer.items` (for whatever reasons)? – frans Mar 14 '22 at 11:44
  • You cannot "synchronise" it, you can only wait for reading all the files/strings. See [this](https://stackoverflow.com/q/31426740/1048572) for waiting for all data and [that](https://stackoverflow.com/q/22519784/1048572) for how to build the promises. Just slapping `.then()` on the result of a function expecting a callback doesn't work. (Though tbh, they should have designed the API to simply return a promise) – Bergi Mar 14 '22 at 11:48
  • @frans FWIW I ran into the same problem and came to the exact same solution as you detailed in your update. – Axionatic Sep 17 '22 at 08:05

0 Answers0