I am new to javascript and web development a whole, so I am sure there is a concept I am missing here and hopefully an easy way to fix this. I am using pointer events for drag and drop and it seems that when using touch it works fine. But when using a mouse, the draggable div will have unpredictable behavior. If you move the mouse quickly while dragging the div, the cursor will loose the element and the div will get stuck in limbo. What is also strange, and possibly related, is that the pointermove event will fire without first having pointerdown fire.
I thought some type of default behavior was causing this issue so I have added a pointercancel event (which didn't help). And I have added e.preventDefault() to the handlers but this didn't help either.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="styles.css">
<script src="script.js" defer></script>
<title>Document</title>
</head>
<body>
<div>
<div class="container" id="lineup">
<div class=draggable id="player1" draggable="false">1</div>
<div class=draggable id="player2" draggable="false">2</div>
<div class=draggable id="player3" draggable="false">3</div>
</div>
</div>
</body>
</html>
* {
box-sizing: border-box;
}
body {
font-family: Arial, Helvetica, sans-serif;
margin: 0;
}
#player{
border: 2px solid white;
border-radius: 6px;
margin: 2px;
}
.dragging{
border: 5px solid white;
border-radius: 6px;
margin: 2px;
color:blue;
}
.draggable.dragging {
opacity: .5;
}
.draggable{
height: 50px;
width: 50px;
border: 1px solid #000;
border-radius: 10px;
float: left;
background: blueviolet;
margin: 10px;
touch-action: none;
}
const draggables = document.querySelectorAll('.draggable')
draggables.forEach(draggable => {
draggable.addEventListener('pointerdown', e => {
e.preventDefault()
console.log(e)
draggable.classList.add('dragging')
draggable.setAttribute('draggable', true)
})
draggable.addEventListener('pointermove', e => {
console.log(e)
e.preventDefault()
const player = document.getElementById(e.target.attributes.id.nodeValue)
if (player == null) return
if (player.classList.contains('dragging')) {
positionPlayer(e, player)
} else {
return
}
})
draggable.addEventListener('pointerup', e => {
console.log(e)
e.preventDefault()
draggable.classList.remove('dragging')
draggable.setAttribute('draggable', false)
})
draggable.addEventListener('pointercancel', e => {
console.log(e)
e.preventDefault()
})
})
function positionPlayer(e, player) {
e.preventDefault()
player.style.position = 'absolute'
player.style.left = `${e.pageX-player.clientWidth/2}px`
player.style.top = `${e.pageY-player.clientWidth/2}px`
console.log(e)
}
pointermoveis sent even if the mouse button isn't down, so you need to track the mouse up/down if you want to do drag and drop. Also,pointermovegets sent to the element the mouse is over, not the element the drag was started on. To fix that, you need "pointer capture". I have an example for both of these (the up/down flag, and the capture) on my page about pointer events for dragging, and I have examples you can copy into jsfiddle.