How to start mouseover event while dragging

29,347

Solution 1

Here is an example using the X-Y coordinate solution.

Working example on jsfiddle

The example can be improved, but is a good starting point.

Simply keeps track of the mouse location and checks if it appears to be inside any bounding boxes of the droppable objects. Hence, if the mouseup event fires on any one of them, dragged object is dropped.

You can also use the coordinates of the object you are dragging for detecting if its on a droppable box, but it requires a little more code for finding the bounding box coords and using the mouse is enough for me.

The code uses jQuery but no jQueryUI. I tested in Chrome, Firefox and Opera, but not IE :)

I'm also adding the code to here if jsfiddle is not accessible.

HTML

<p>Drag orange boxes to grey ones</p>
<div class="droppable"></div>
<div class="droppable"></div>
<div class="droppable"></div>
<div class="droppable"></div>

<div class="draggable"></div>
<div class="draggable"></div>
<div class="draggable"></div>

CSS

.droppable {
    width:50px;
    height:50px;
    float: left;
    background-color: #DDD;
    margin: 5px;
}

.draggable {
    width:40px;
    height:40px;
    float: right;
    background-color: #FC0;
    margin: 5px;
    cursor: pointer;
}

.dropped {
    background-color: #FC0;
}

.somethingover {
    background-color: #FCD;
}

JS

var dragged, mousex, mousey, coordinates = [];

var continueDragging = function(e) {
    // Change the location of the draggable object
    dragged.css({
        "left": e.pageX - (dragged.width() / 2),
        "top": e.pageY - (dragged.height() / 2)
    });

    // Check if we hit any boxes
    for (var i in coordinates) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                // Lets change the background color
                coordinates[i].dom.addClass("somethingover");
            }
        } else {
            // Nope, we did not hit any objects yet
            coordinates[i].dom.removeClass("somethingover");
        }
    }

    // Keep the last positions of the mouse coord.s
    mousex = e.pageX;
    mousey = e.pageY;
}

var endDragging = function(e) {
    // Remove document event listeners
    $(document).unbind("mousemove", continueDragging);
    $(document).unbind("mouseup", endDragging);

    // Check if we hit any boxes
    for (var i in coordinates) {
        if (mousex >= coordinates[i].left && mousex <= coordinates[i].right) {
            if (mousey >= coordinates[i].top && mousey <= coordinates[i].bottom) {
                // Yes, the mouse is on a droppable area
                droptarget = coordinates[i].dom;
                droptarget.removeClass("somethingover").addClass("dropped");
                dragged.hide("fast", function() {
                    $(this).remove();
                });
            }
        }
    }

    // Reset variables
    mousex = 0;
    mousey = 0;
    dragged = null;
    coordinates = [];
}

var startDragging = function(e) {
    // Find coordinates of the droppable bounding boxes
    $(".droppable").each(function() {
        var lefttop = $(this).offset();
        // and save them in a container for later access
        coordinates.push({
            dom: $(this),
            left: lefttop.left,
            top: lefttop.top,
            right: lefttop.left + $(this).width(),
            bottom: lefttop.top + $(this).height()
        });
    });

    // When the mouse down event is received
    if (e.type == "mousedown") {
        dragged = $(this);
        // Change the position of the draggable
        dragged.css({
            "left": e.pageX - (dragged.width() / 2),
            "top": e.pageY - (dragged.height() / 2),
            "position": "absolute"
        });
        // Bind the events for dragging and stopping
        $(document).bind("mousemove", continueDragging);
        $(document).bind("mouseup", endDragging);
    }
}

// Start the dragging
$(".draggable").bind("mousedown", startDragging);

Solution 2

In all presented answers, I don't see the most simple and obvious one (maybe I'm missing something in OP question). But, if someone stumble upon this later, and needs fast and simple solution in pure JS..

You do it by changing element className ondragover, and changing back to original class ondragleave

my_element.ondragover = function(ev) {  
 ev.preventDefault();  
 this.className = 'myElem_dragover';  
}  
my_element.ondragleave = function(ev) {  
 ev.preventDefault();  
 this.className = 'myElem_orig';  
}

CSS

.myElem_orig {     //this is your initial class for element
  top: 30px;
  left: 20px;
  .....
  background-color: blue;  
}  

.myElem_orig:hover {   //this is hover state, just changing bg color
  background-color: red;
}

.myElem_dragover { //new class, needs all attributes from original class
  top: 30px;
  left: 20px;
  ........ 
  background-color: red; //behaves the same like hover does
}

edit:
forgot to mention, you need to bring back original class ondrop too, otherwise div will stay in dragover class

Solution 3

There are two basic ways you can do this:

  1. track mousemove and react to x/y coordinates
  2. have a transparent target that has a higher z-index than the drag container

First option doesn't really use the mouseover event at all, but will give you the same net result.

Be aware that some browsers (ie) won't trigger mouseover on transparent elements, so you have to fake it by setting a background image that is transparent or setting a random image as background and positioning it outside the element like this:

element {
 background: url(/path/to/img) no-repeat -10000px 0;
}

Solution 4

jQuery-ui has a droppable plugin for this.

The plugin, when used with a draggable element will trigger dropover events, which can be bound to any action you require.

See Mottie's answer to this question (demo included)

Share:
29,347
V.Rashkov
Author by

V.Rashkov

Updated on July 05, 2022

Comments

  • V.Rashkov
    V.Rashkov almost 2 years

    When I drag an element over another div on which I have a mouseover event, the event doesn't trigger. However, it works if I hover over it without dragging.

    Is there a way to detect hover events on an element if I drag another one over it?

  • Purag
    Purag over 12 years
    That might interfere with the element being dragged, depending on how he has it set up.
  • OrganicPanda
    OrganicPanda about 12 years
    I was a bit skeptical at first but this technique has worked very well for me - thanks very much!
  • Abc
    Abc about 9 years
    I think this question pre-dates widespread use of the built-in HTML draggable attribute, which is definitely the easiest way to go unless you're doing custom behaviors.
  • Wellyngton
    Wellyngton about 8 years
    That's what I was looking for, not any custom behavior. Thanks.
  • Lucas
    Lucas over 7 years
    I agree with greg. This answer is excellent now-a-days and will get popular very soon I feel.
  • Kelderic
    Kelderic almost 7 years
    One downside to the dragover/dragout is that it can't tell, as far as I know, which side of an element you are hovering over. Say it's a list of items that you are drag'n'drop sorting, if you hover over the top half of an item, you'd want to dragged content to be placed ABOVE that item. But you can only see that you are hovering on it, now the location within.
  • Wolf War
    Wolf War almost 7 years
    @AndyMercer in dragOver event you can test on which half the mouse is, up or down, and according to that make decision. With the object.getBoundingClientRect() you get for instance, bottom border and subtract mouse Y coordinate from it. You get value that is bigger or lover than object height/2
  • Kelderic
    Kelderic almost 7 years
    Kinda funny, I just posted a question asking about that, and your comment here may have just answered it. I'll investigate the dragover event some more, thanks.
  • Wolf War
    Wolf War almost 7 years
    @AndyMercer getBoundingClientRect() is not part of the drag&drop API if you were thinking that. It's just that in dragOver event you can test many things, on of them is bounding rectangle, and calculate position with pure math.
  • Kelderic
    Kelderic almost 7 years
    Yep, that is the logic I used. I gave you credit in the answer to the question I posted. stackoverflow.com/questions/44415228/…, thanks mate!