29

I'm trying to get drag'n'drop to work, but I seem to be completely missing how the getData/setData works.

I'm using this code (http://jsfiddle.net/ASKte/218/)

var el = angular.element(document.getElementById('drag'));
el.attr("draggable", "true");
el.bind("dragstart", function(e) {
    e.dataTransfer.setData('text', 'Where have you gone?!?!')         
});

var target = angular.element(document.getElementById('drop'));
target.bind("dragover", function(e) {
    if (e.preventDefault) {
        e.preventDefault(); // Necessary. Allows us to drop.
    }
    return false;
});

target.bind("dragenter", function(e) {
    console.debug(e.dataTransfer.types);
    console.debug(e.dataTransfer.getData('text'));
});

I'm using AngularJS here because this is a snippet of a much larger piece of code.

For some reason when dragging the top square on the bottom square, the value of getData('text') is always empty, but I have no idea why...

Any ideas?

Thanks in advance.

Robba
  • 7,684
  • 12
  • 48
  • 76

2 Answers2

80

The data is only available on drop, this is a security feature since a website could grab data when you happen to be dragging something across the webpage.

var el = angular.element(document.getElementById('drag'));
el.attr("draggable", "true");
el.bind("dragstart", function(e) {
    e.dataTransfer.setData('text', 'Where have you gone?!?!')         
});

var target = angular.element(document.getElementById('drop'));
target.bind("dragover", function(e) {
    if (e.preventDefault) {
        e.preventDefault(); // Necessary. Allows us to drop.
    }
    return false;
});

target.bind("drop", function(e) {
    console.debug(e.dataTransfer.types);
    console.debug(e.dataTransfer.getData('text'));
});  
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="drag" style="width: 100px; height: 100px; background-color: blue;"></div>

<div id="drop" style="width: 100px; height: 100px; background-color: green;"></div>
Musa
  • 96,336
  • 17
  • 118
  • 137
  • 6
    Ah I see, that's too bad then. I'd wanted to do some dynamic style changing based on what's being dragged. I guess I'll have to figure out some other solution then. – Robba Feb 13 '15 at 00:10
  • 2
    @Robba have you found a solution for that? – Bernardo Dal Corno Jan 28 '17 at 19:08
  • 14
    @Z.Khullah you can inspect dataTransfer.types during dragover, which will be an array of the strings you have passed as the first parameter to setData. So you could for example call setData('text', 'my drop data') and setData('dropzone1', 'dummy'), then inspect the types array on dragover to see if it contains dropzone1 or dropzone2, and you'll know which dropEffect to set. – Brian S Dec 21 '18 at 06:37
  • 1
    @BrianS suggestion is the right answer, super-easy to implement! Be aware that the .types are automatically lowerCased, be careful to compare the types with your id in lowercase (or be sure your id is lowercase) – François Dispaux May 18 '20 at 12:20
  • 1
    This is such an annoying limitation. I understand the security risks but this should be optional with additional security measures like limiting who can see what’s being dragged (eg only the same URL/viewport where drag was initiated). Use case: I need to know which specific item (not just what type of item) is being dragged to decide if it can be dropped on another to give the user feedback while they're hovering. – Pawel Decowski Sep 29 '21 at 12:11
  • @BrianS or @Musa: this answer is worth being completed by a new answer or improved You can't use "getData" in the drag event, however using `dataTransfer.types`, potentially with a custom mime-type of you call "setData" manually, seems relevant as the intent here is to check whether the drop is acceptable or not. – Eric Burel Nov 29 '22 at 15:51
11

If you really need to access data in other drop-events, use dataTransfer.types. Use JSON.parse, JSON.stringify and encode/decode uppercase characters to get around dataTransfer.types getting lower cased.

Setting data:

event.dataTransfer.setData(encodeUpperCase(JSON.stringify(value)), '');

Getting data:

const data = JSON.parse(decodeUpperCase(event.dataTransfer.types[0]))

Where the encoding/decoding goes something like this:

  const UPPERCASE_PREFIX = '^{';
  const UPPERCASE_SUFFIX = '}^';

  function encodeUpperCase(str: string): string {
    return str.replace(/([A-Z]+)/g, `${UPPERCASE_PREFIX}$1${UPPERCASE_SUFFIX}`);
  }

  function decodeUpperCase(str: string): string {
    const escapeRegExp = (escape: string) => ['', ...escape.split('')].join('\\');

    return str.replace(
      new RegExp(`${escapeRegExp(UPPERCASE_PREFIX)}(.*?)${escapeRegExp(UPPERCASE_SUFFIX)}`, 'g'),
      (_, p1: string) => p1.toUpperCase()
    );
  }

It works, but you might summon Cthulhu in the process.

blid
  • 971
  • 13
  • 22