29

I need to set the icon for cursor when a user is dragging DIV (red div in the following example).

I have tried several attempt, including using CSS cursor:move and event.dataTransfer.dropEffect with no success, as the icon always show up a "crossed circle".

Any ideas how to solve this issue using HTML5 drag-and-drop API?

http://jsbin.com/hifidunuqa/1/

 <script>
        window.app = {
            config: {
                canDrag: false,
                cursorOffsetX: null,
                cursorOffsetY: null
            },
            reset: function () {
                this.config.cursorOffsetX = null;
                this.config.cursorOffsetY = null;
            },
            start: function () {
                document.getElementById('target').addEventListener('dragstart', function (event) {
                    console.log('+++++++++++++ dragstart')
                    this.config.cursorOffsetX = event.offsetX;
                    this.config.cursorOffsetY = event.offsetY;
                    this.adjustPostion(event);
                    event.dataTransfer.effectAllowed = 'move';
                    event.dataTransfer.dropEffect = 'move';
                }.bind(this));
                document.getElementById('target').addEventListener('drag', function (event) {
                    console.log('+++++++++++++ drag')
                    this.adjustPostion(event);
                }.bind(this));
                document.getElementById('target').addEventListener('dragend', function (event) {
                    console.log('+++++++++++++ dragend')
                    this.reset();
                }.bind(this));;
            },
            adjustPostion: function (event) {
                if (event.pageX <= 0 || event.pageY <= 0) {
                    console.log('skipped');
                    return;
                }
                var elm = document.getElementById('target');
                elm.style.left = (event.pageX - this.config.cursorOffsetX) + 'px';
                elm.style.top = (event.pageY - this.config.cursorOffsetY) + 'px';
                console.log(event.pageX);
                console.log(event.pageY);
            }

        };
    </script>
GibboK
  • 71,848
  • 143
  • 435
  • 658
  • 1
    what about mousedown event? – Abdul Ahmad May 05 '15 at 11:49
  • yes jquery solutions are also ok. thanks – GibboK May 05 '15 at 11:49
  • why not just add a class on drag start and remove it on drag end? – Pete May 05 '15 at 11:55
  • 2
    @Pete Will that work? The element already has a `cursor: move !important;` style on it *all the time*. It doesn't seem likely that adding such a style at drag-time only will change the behavior. – apsillers May 05 '15 at 11:57
  • Is this any use? http://jsfiddle.net/vL6htL5m/ – lharby May 05 '15 at 12:00
  • 1
    @lharby Your example does not use the HTML5 drag-and-drop API, which is the source of this particular problem. – apsillers May 05 '15 at 12:02
  • @apsillers correct I need to make it work with HTML5 drag-and-drop API. Thanks all for your help. – GibboK May 05 '15 at 12:03
  • @apsillers good point, I wrote that based on the above code, it's one of the reasons why adding the mcve in the question itself is always good – Pete May 05 '15 at 12:16
  • 1
    @GibboK [this may help you](http://www.html5rocks.com/en/tutorials/dnd/basics/) - see the dragging properties section. It shows you can add your own icon whilst dragging, but it seems to go along with the no-entry circle (which may be compulsory as it shows you are not allowed to drop your object where you are) – Pete May 05 '15 at 12:31
  • Tried :active? Essentially, that's it. –  Jun 06 '16 at 05:31
  • You could just check if this works for you: http://stackoverflow.com/questions/10119514/html5-drag-drop-change-icon-cursor-while-dragging – abc Jun 22 '16 at 12:13

4 Answers4

10

use mousedown and mousemove

window.app = {
  dragging: false,
  config: {
    canDrag: false,
    cursorOffsetX: null,
    cursorOffsetY: null
  },
  reset: function () {
    this.config.cursorOffsetX = null;
    this.config.cursorOffsetY = null;
  },
  start: function () {
    document.getElementById('target').addEventListener('mousedown', function (event) {
      console.log('+++++++++++++ dragstart');
      this.dragging = true;
      this.config.cursorOffsetX = event.offsetX;
      this.config.cursorOffsetY = event.offsetY;
      this.adjustPostion(event);
    }.bind(this));
    document.getElementById('target').addEventListener('mousemove', function (event) {
      if (this.dragging) {
        console.log('+++++++++++++ drag');
        event.target.style.cursor = 'move'; 
        this.adjustPostion(event);
      }
    }.bind(this));
    document.getElementById('target').addEventListener('mouseup', function (event) {
      console.log('+++++++++++++ dragend');
      this.dragging = false;
      event.target.style.cursor = 'pointer'; 
      this.reset();
    }.bind(this));
  },
  adjustPostion: function (event) {
    if (event.clientX <= 0 || event.clientY <= 0) {
      console.log('skipped');
      return;
    }
    var elm = document.getElementById('target');
    elm.style.left = (event.clientX - this.config.cursorOffsetX) + 'px';
    elm.style.top = (event.clientY - this.config.cursorOffsetY) + 'px';
    console.log(event.pageX);
    console.log(event.pageY);
  }

};
#target {
            position: absolute;
            top: 100px;
            left: 100px;
            width: 400px;
            height: 400px;
            background-color: red;
            -moz-user-select: none;
            -ms-user-select: none;
            -webkit-user-select: none;
            user-select: none;
        }

        #ui1 {
            position: absolute;
            top: 50px;
            left: 50px;
            width: 100px;
            height: 400px;
            background-color: blue;
            z-index: 100;
        }

        #ui2 {
            position: absolute;
            top: 50px;
            left: 550px;
            width: 100px;
            height: 400px;
            background-color: green;
            z-index: 100;
        }
<!-- simulate -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>title</title>
</head>
<body onload="window.app.start();">
    <div id="ui1"></div>
    <div id="ui2"></div>
    <div id="target"></div>
</body>
</html>
Quentin Roy
  • 7,677
  • 2
  • 32
  • 50
Ihsan
  • 351
  • 1
  • 4
  • 13
  • Why do you need to control cursor offsets? The example ran perfectly, couldn't replicate in my app though, don't know why. – Bernardo Dal Corno Dec 05 '16 at 23:15
  • to make sure the element does not jump because when you clicked the element you clicked it inside the element, and you need that cursor position offset inside the element. – Ihsan Dec 16 '17 at 17:11
6

Do you actually need the Drag API? I found that I was using the Drag API because I was having trouble with the reliability of mouse events (mouseups not being captured, for example).

The Drag API is only for drag-and-drop functionality, and, if you're simply fiddling with the reliability of your clicking and pointing events, a new API, .setPointerCapture is made to handle these cases more effectively. Here's the minimal viable way to achieve this:

el.onpointerdown = ev => {
    el.onpointermove = pointerMove 
    el.setPointerCapture(ev.pointerId)
}

pointerMove = ev => {
    console.log('Dragged!')
}

el.onpointerUp = ev => {
    el.onpointermove = null
    el.releasePointerCapture(ev.pointerId)
}

Beautifully, you will maintain full control over your cursor's display style.

nikk wong
  • 8,059
  • 6
  • 51
  • 68
3

I didn't care about a particular cursor, I just wanted to get rid of the "crossed circle" one. My solution was to include dragover event (with following function) to all elements, that already had dragenter, dragstart and dragend events.

function dragover(event)
{
    event.dataTransfer.dropEffect = "move";
    event.preventDefault();
}
mikiqex
  • 5,023
  • 2
  • 24
  • 22
1

Adding event.dataTransfer.setData(); should solve the problem. Once the element is recognized as draggable the browser will add a move cursor automatically once you drag. Of course, you will have to remove all other cursor: move declarations to see the cursor changing while dragging.

Minimal example:

document.getElementById('target').addEventListener('dragstart', function (event) {
  event.dataTransfer.setData( 'text/plain', '' );
}.bind(this));

If you still want to change the icon (e.g. to use a custom drag icon), you could access the element style using event.target.style.cursor.

For more info see MDN Drag & Drop and MDN Recommended Drag Types

  • 3
    Unfortunately it seems that does not work. using event.target.style.cursor='none'; does not change the cursor. Could you please apply your solution to my jsfiddle and send me the link? Thanks – GibboK May 06 '15 at 06:02
  • Honestly, I am not exactly sure anymore what you are trying to do. If your goal is to change/remove the icon during drag, I don't know if this is possible. It seems CSS changes only affect elements, that are not dragged. There are a few pointers: http://stackoverflow.com/questions/6079346/javascript-how-can-i-set-the-cursor-during-a-drag-drop-operation-on-a-website https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations –  May 06 '15 at 07:33
  • 2
    Correct, I need to change the icon during dragging using HTML5 drag-and-drop API. Thanks for your time on this. – GibboK May 06 '15 at 07:41