Make HTML Element Draggable
Sample code to make an HTML element draggable.
This is different from how to implement html drag and drop API. The goal of this sample code is to move one html element freely inside window.
Codebase
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Draggable</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="draggable">Draggable</div>
<script src="app.js"></script>
</body>
</html>
style.css
@charset "utf-8";
.draggable {
position: absolute;
border: 1px solid #ddd;
border-radius: 2px;
background: #f0f0f0;
display: inline-block;
padding: 8px;
cursor: grab;
}
.draggable:active {
cursor: grabbing;
}
/* Only for demo */
html, body {
width: 100%;
height: 100%;
}
body {
margin: 0;
font-family: sans-serif;
}
app.js
const draggable = document.querySelector('.draggable')
const unit = 'px'
let diffX, diffY
function startDrag(e) {
e.preventDefault()
const { x, y } = draggable.getBoundingClientRect()
diffX = e.x - x
diffY = e.y - y
document.onmousemove = drag
}
function drag(e) {
e.preventDefault()
draggable.style.left = (e.x - diffX) + unit
draggable.style.top = (e.y - diffY) + unit
}
function endDrag() {
document.onmousemove = null
}
draggable.onmousedown = startDrag
draggable.onmouseup = endDrag
Why Not Drag & Drop API
Bit of notes why the sample code don’t use drag and drop api, more specifically the dragstart, drag, and dragend events.
If we rewrite the js code above with drag and drop api, the code would be like this:
const draggable = document.querySelector('.draggable')
const unit = 'px'
let diffX, diffY
function startDrag(e) {
const { x, y } = draggable.getBoundingClientRect()
diffX = e.x - x
diffY = e.y - y
draggable.ondrag = drag
}
function drag(e) {
draggable.style.left = (e.x - diffX) + unit
draggable.style.top = (e.y - diffY) + unit
}
function endDrag() {
draggable.ondrag = null
}
draggable.ondragstart = startDrag
draggable.ondragend = endDrag
This code has the following issues:
One: it displays the dragged element and its projection.
This is not much recognized with the implementation above, but if we don’t change the position of dragged element as we move a mouse, the dragged element and projection would look like:
Not only is this projection unnecessary as we just want to move the element but also can be displayed outside browser window.
Furthermore, if we take a closer look, there are a few milliseconds of misalignment between the element and its projection as we move a mouse.
Two: the last DragEvent of drag() call has properties of x:0 and y:0, which positions the dragged element to the left top of window overflown.
Nevertheless, we do have more control on the end implementation with MouseEvent than with DragEvent because the latter is more abstractive and designed for the specific purpose.