I am currently developing a drag and drop interface and would like to implement a feature that allows users to return a dragged object to its original position by right-clicking.
In the demo below, the blue ball can be dragged to any of the 3 boxes. I added an event listener for a right-click mouseup event.
Desired outcome:
Anytime when I am dragging the ball, a right mouse click will end the drag event and return the ball to the original square it was dragged from.
Actual outcome:
When I am dragging the ball, a right mouse click does nothing. Only after I drop the ball does a right-click return the ball to the original square.
Potential problem identified
After testing, this issue may be due to the fact that additional mouse event listeners are not active during the 'dragging' phase. What should I do?
Code
const boxes = document.getElementsByClassName("box");
const ball = document.getElementById("ball");
for (var i = 0; i < boxes.length; i++) {
boxes[i].addEventListener("dragover", function(event) {
event.preventDefault();
});
boxes[i].addEventListener("drop", function(event) {
event.preventDefault();
this.appendChild(ball);
});
}
var originalBox;
ball.setAttribute("draggable", true);
ball.addEventListener("dragstart", function(event) {
originalBox = ball.closest(".box");
});
document.addEventListener('mouseup', (event) => {
// (event.button === 2) checks if the right mouse button was clicked
if (event.button === 2) {
originalBox.appendChild(ball);
}
});
.box {
width: 100px;
height: 100px;
border: 1px solid black;
}
#ball {
width: 90px;
height: 90px;
border-radius: 100%;
background-color: DodgerBlue;
}
<div class="box">
<div id="ball"></div>
</div>
<div class="box">
</div>
<div class="box">
</div>
I managed to write a workaround by following the guide here: Drag'n'Drop with mouse events
Instead of using ball.setAttribute("draggable", true);
, I am using ball.onmousedown = function(event)
.
The movement of the ball is then tracked using
moveAt(event.pageX, event.pageY);
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
Hence, the drag event is simulated, and I do not have to use the native drag and drop functions. This way, listeners for onMouseUp
can be added.
Finally, to make right-click return the ball to the original position, I have added the following function onMouseUp(event)
to simulate the "drop" event:
if (event.button === 0) {
currentDroppable.append(ball);
} else {
originalSquare.append(ball);
}
event.button === 0
refers to the left mouse button. The code above means that when the left mouse button is the one that is released, the ball is appended to the mouse's position. If not (i.e. if the right button or middle button is clicked), the ball is appended to the original position.
I have adjusted the code snippet.
Code
let ball = document.getElementById('ball')
let currentDroppable = null;
let originalSquare = null;
document.body.addEventListener("contextmenu", e => e.preventDefault());
document.body.ondragstart = function() {
return false;
};
ball.onmousedown = function(event) {
if (event.button === 0) {
originalSquare = this.parentElement;
let shiftX = event.clientX - ball.getBoundingClientRect().left;
let shiftY = event.clientY - ball.getBoundingClientRect().top;
ball.style.position = 'absolute';
moveAt(event.pageX, event.pageY);
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
ball.hidden = true;
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
ball.hidden = false;
if (!elemBelow) return;
let droppableBelow = elemBelow.closest('.box');
if (currentDroppable != droppableBelow) {
currentDroppable = droppableBelow;
}
}
function onMouseUp(event) {
document.removeEventListener('mousemove', onMouseMove);
if (currentDroppable) {
if (event.button === 0) {
currentDroppable.append(ball);
} else {
originalSquare.append(ball);
}
} else {
originalSquare.append(ball);
};
document.removeEventListener('mouseup', onMouseUp);
ball.style.position = null;
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
}
}
ball.ondragstart = function() {
return false;
};
.box {
width: 100px;
height: 100px;
border: 1px solid black;
}
#ball {
width: 90px;
height: 90px;
cursor: default;
border-radius: 100%;
background-color: DodgerBlue;
z-index: 2;
}
<div class="box">
<div id="ball"></div>
</div>
<div class="box">
</div>
<div class="box">
</div>