How to start mouseover event while dragging
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:
- track
mousemove
and react to x/y coordinates - 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)
Solution 5:
Modifing a bit the code posted by emrahgunduz, specifically the for loop, you can also manage nested droppable area.
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 = coordinates.length - 1; i >= 0; i--) {
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
$('.droppable').removeClass("somethingover");
coordinates[i].dom.addClass("somethingover");
break;
}
} 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 = coordinates.length - 1; i >= 0; i--) {
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);