I'm using html5's drag and drop functionalities to rearrange dom elements on screen - I attach css behavior to the various states of dragging and dropping when I do this, but the problem I'm experiencing is that the hover state remains even after I've dragged, dropped, and moused out of a DOM element. Here's my code:
JAVASCRIPT:
function addDragListeners(){
$('.segmentListItem').each(function(index){
$(this)[0].addEventListener('dragstart',handleDragStart,false); //rollover for current
$(this)[0].addEventListener('drop',handleDrop,false); //drops dragged element
$(this)[0].addEventListener('dragover',handleDragOver,false); //allows us to drop
$(this)[0].addEventListener('dragenter',handleDragEnter,false); //rollover for target
$(this)[0].addEventListener('dragleave',handleDragLeave,false); //sets dragged item back to normal
$(this)[0].addEventListener('dragend',handleDragEnd,false); //sets all back to normal
});
}
function handleDragEnter(e) {
// this / e.target is the current hover target.
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over'); // this / e.target is previous target element.
}
function handleDragEnd(e){
$('.segmentListItem').removeClass('over'); //removes the over class on all elements
}
function handleDragStart(e){
draggedItem = this;
e.dataTransfer.effectAllowed = 'move';
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object.
return false;
}
function handleDrop(e){
if (e.stopPropagation) {
e.stopPropagation();
}
if (draggedItem != this) { //MH - swap if we're not dragging the item onto itself
var draggedIndex = $('.segmentListItem').index($(draggedItem));
var targetIndex = $('.segmentListItem').index($(this));
if (draggedIndex > targetIndex){
$(draggedItem).insertBefore($(this));
} else {
$(draggedItem).insertAfter($(this));
}
}
return false;
}
CSS:
a { border-radius: 10px; }
a:hover { background: #ccc; }
.segmentListItem { text-align:center; width: 50px; margin-right: 5px; font-size: 16px; display:inline-block; cursor:move; padding:10px; background: #fff; user-select: none; }
.segmentListItem.over { background: #000; color: #fff; }
According to https://bugs.webkit.org/show_bug.cgi?id=134555 this used to be a bug. However, it must have been fixed in the meantime, as it cannot be reproduce anymore in modern browsers. The only browser where I could still replicate it was IE11.
You can replace the CSS :hover
with a .hover
class toggled from JS, in order to better control the hover state:
document.querySelectorAll('.segmentListItem a').forEach(function (item) {
item.addEventListener('mouseenter', function () {
this.classList.add('hover');
});
item.addEventListener('mouseleave', function () {
this.classList.remove('hover');
});
});
Code snippet below:
function addDragListeners() {
$(".segmentListItem").each(function(index) {
$(this)[0].addEventListener("dragstart", handleDragStart, false); //rollover for current
$(this)[0].addEventListener("drop", handleDrop, false); //drops dragged element
$(this)[0].addEventListener("dragover", handleDragOver, false); //allows us to drop
$(this)[0].addEventListener("dragenter", handleDragEnter, false); //rollover for target
$(this)[0].addEventListener("dragleave", handleDragLeave, false); //sets dragged item back to normal
$(this)[0].addEventListener("dragend", handleDragEnd, false); //sets all back to normal
});
}
function handleDragEnter(e) {
// this / e.target is the current hover target.
this.classList.add("over");
}
function handleDragLeave(e) {
this.classList.remove("over"); // this / e.target is previous target element.
}
function handleDragEnd(e) {
e.preventDefault();
$(".segmentListItem").removeClass("over"); //removes the over class on all elements
}
function handleDragStart(e) {
draggedItem = this;
e.dataTransfer.effectAllowed = "move";
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to drop.
}
e.dataTransfer.dropEffect = "move"; // See the section on the DataTransfer object.
return false;
}
function handleDrop(e) {
if (e.stopPropagation) e.stopPropagation();
if (draggedItem != this) {
//MH - swap if we're not dragging the item onto itself
var draggedIndex = $(".segmentListItem").index($(draggedItem));
var targetIndex = $(".segmentListItem").index($(this));
if (draggedIndex > targetIndex) {
$(draggedItem).insertBefore($(this));
} else {
$(draggedItem).insertAfter($(this));
}
}
return false;
}
// JS fix starts here:
document.querySelectorAll('.segmentListItem a').forEach(function(item, idx) {
item.addEventListener('mouseenter', function() {
this.classList.add('hover');
});
item.addEventListener('mouseleave', function() {
this.classList.remove('hover');
});
});
// and ends here. Comment these lines, and uncomment the `a:hover` rule in CSS in order to see the initial code
addDragListeners();
a {
border-radius: 10px;
}
/* a:hover {
background: #ccc;
} */
.segmentListItem {
text-align: center;
width: 50px;
margin-right: 5px;
font-size: 16px;
display: inline-block;
cursor: move;
padding: 10px;
background: #fff;
user-select: none;
}
.segmentListItem.over {
background: #000;
color: #fff;
}
.hover {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<ul>
<li class="segmentListItem">
<a href="#">test1</a>
</li>
<li class="segmentListItem">
<a href="#">test2</a>
</li>
<li class="segmentListItem">
<a href="#">test3</a>
</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