I am working on a sortable list, to avoid having to manually type in sorting order numbers in a database. It works via HTML5's drag'n'drop functionality, i.e. the new drag*
events in Javascript.
I currently have it working for the most part. I can click, and drag, and it'll sort itself.
From what I can tell, the drop
, along with the dragstart
and dragend
events, only are aware of the element they are going into. They can't tell if the mouse is on the top half of the dropzone, or the bottom half.
What I'd like, is when I'm hovering over the top half of a list item, for the dragged content to be placed ABOVE the item. Then if I'm hovering over the bottom half, for the dragged content to be placed BELOW the item.
In the screencap below, I show a working (simplified) example of my code. I'm using a border-bottom
on the drop target to show that it's the target. Notice how when "Item 1" is above "Item 2", "Item 2" is lit up on the bottom, regardless of if I'm hovering over the top half or bottom half.
Code
var dragging = null;
document.addEventListener('dragstart', function(event) {
dragging = event.target;
event.dataTransfer.setData('text/html', dragging);
});
document.addEventListener('dragover', function(event) {
event.preventDefault();
});
document.addEventListener('dragenter', function(event) {
event.target.style['border-bottom'] = 'solid 4px blue';
});
document.addEventListener('dragleave', function(event) {
event.target.style['border-bottom'] = '';
});
document.addEventListener('drop', function(event) {
event.preventDefault();
event.target.style['border-bottom'] = '';
event.target.parentNode.insertBefore(dragging, event.target.nextSibling);
});
ul {
margin:0;
padding:0
}
li {
cursor:move;
display:block;
padding:20px 10px;
background:white;
border-bottom:solid 1px gray;
}
<ul>
<li draggable="true" class="sortable-bulk">List Item 1</li>
<li draggable="true" class="sortable-bulk">List Item 2</li>
<li draggable="true" class="sortable-bulk">List Item 3</li>
<li draggable="true" class="sortable-bulk">List Item 4</li>
<li draggable="true" class="sortable-bulk">List Item 5</li>
<li draggable="true" class="sortable-bulk">List Item 6</li>
<li draggable="true" class="sortable-bulk">List Item 7</li>
<li draggable="true" class="sortable-bulk">List Item 8</li>
<li draggable="true" class="sortable-bulk">List Item 9</li>
<li draggable="true" class="sortable-bulk">List Item 10</li>
</ul>
Is there a way I can have it drop either above or below, not always below, depending on the mouse position while dragging?
For example, to drag-and-drop an object, such as an icon, you first move your mouse cursor over it. Then, press and hold down the left mouse button, move the object to the location you desire, and release the mouse button to set it down.
Now HTML 5 came up with a Drag and Drop (DnD) API that brings native DnD support to the browser making it much easier to code up. HTML 5 DnD is supported by all the major browsers like Chrome, Firefox 3.5 and Safari 4 etc.
9) The drop event fires when the user releases the mouse button while dragging an object.
So in a twist of fate, a comment response on another question pointed me to the answer here. wolf-war deserves credit here for pointing me to the right event and method.
Anyway, on to the answer. The solution lies in using the dragover
event, rather than using the dragenter
event. dragover
keeps firing as long as you are hovering.
We get rid of the dragenter
code:
document.addEventListener('dragenter', function(event) {
event.target.style['border-bottom'] = 'solid 4px blue';
});
Replace it with:
document.addEventListener('dragover', function(event) {
event.preventDefault();
var bounding = event.target.getBoundingClientRect()
var offset = bounding.y + (bounding.height/2);
if ( event.clientY - offset > 0 ) {
event.target.style['border-bottom'] = 'solid 4px blue';
event.target.style['border-top'] = '';
} else {
event.target.style['border-top'] = 'solid 4px blue';
event.target.style['border-bottom'] = '';
}
});
Then in the drop
section, we check to see which border the drop target has, and use that to insert the dragged content either above or below.
var dragging = null;
document.addEventListener('dragstart', function(event) {
var target = getLI( event.target );
dragging = target;
event.dataTransfer.setData('text/plain', null);
event.dataTransfer.setDragImage(self.dragging,0,0);
});
document.addEventListener('dragover', function(event) {
event.preventDefault();
var target = getLI( event.target );
var bounding = target.getBoundingClientRect()
var offset = bounding.y + (bounding.height/2);
if ( event.clientY - offset > 0 ) {
target.style['border-bottom'] = 'solid 4px blue';
target.style['border-top'] = '';
} else {
target.style['border-top'] = 'solid 4px blue';
target.style['border-bottom'] = '';
}
});
document.addEventListener('dragleave', function(event) {
var target = getLI( event.target );
target.style['border-bottom'] = '';
target.style['border-top'] = '';
});
document.addEventListener('drop', function(event) {
event.preventDefault();
var target = getLI( event.target );
if ( target.style['border-bottom'] !== '' ) {
target.style['border-bottom'] = '';
target.parentNode.insertBefore(dragging, event.target.nextSibling);
} else {
target.style['border-top'] = '';
target.parentNode.insertBefore(dragging, event.target);
}
});
function getLI( target ) {
while ( target.nodeName.toLowerCase() != 'li' && target.nodeName.toLowerCase() != 'body' ) {
target = target.parentNode;
}
if ( target.nodeName.toLowerCase() == 'body' ) {
return false;
} else {
return target;
}
}
ul {
margin:0;
padding:0
}
li {
cursor:move;
display:block;
padding:20px 10px;
background:white;
border-bottom:solid 1px gray;
}
<ul>
<li draggable="true">List Item 1</li>
<li draggable="true">List Item 2</li>
<li draggable="true">List Item 3</li>
<li draggable="true">List Item 4</li>
<li draggable="true">List Item 5</li>
<li draggable="true">List Item 6</li>
<li draggable="true">List Item 7</li>
<li draggable="true">List Item 8</li>
<li draggable="true">List Item 9</li>
<li draggable="true">List Item 10</li>
</ul>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With