Make HTML Element Draggable with Class
This is an extended version of previous example Make Html Element Draggable within Browser Window, turning draggable logic into class.
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-object">
<div class="draggable-subject">Draggable</div>
</div>
<script src="draggable.js" defer></script>
<script src="app.js" defer></script>
</body>
</html>
style.css
@charset "utf-8";
.draggable-object {
position: absolute;
}
.draggable-subject {
cursor: grab;
}
.draggable-subject:active {
cursor: grabbing;
}
/* Only for demo */
html, body {
width: 100%;
height: 100%;
}
body {
margin: 0;
font-family: sans-serif;
}
.draggable-object {
width: 160px;
height: 160px;
border: 1px solid #ddd;
border-radius: 2px;
display: inline-block;
}
.draggable-subject {
background: #f0f0f0;
padding: 4px 8px;
}
style.css is no different from the previous example.
draggable.js
class Draggable {
positionLeft = 0
positionTop = 0
positionLeftMax = 9999
positionTopMax = 9999
unit = 'px'
constructor(objectSelector, subjectSelector) {
const object = document.querySelector(objectSelector)
if (object === null) {
throw new Error('Cannot find an HTMLElement with the selector "' + objectSelector + '".')
}
this.object = object
this.subject = subjectSelector ? document.querySelector(subjectSelector) : object
this.handleMouseDown = this.handleMouseDown.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
this.setMaxPositions()
this.subject.addEventListener('mousedown', this.handleMouseDown)
}
setMaxPositions() {
this.positionLeftMax = innerWidth - this.object.offsetWidth
this.positionTopMax = innerHeight - this.object.offsetHeight
}
clamp(value, min, max) {
return Math.min(max, Math.max(min, value))
}
handleMouseDown(e) {
e.preventDefault()
document.addEventListener('mousemove', this.handleMouseMove)
document.addEventListener('mouseup', this.handleMouseUp)
}
handleMouseMove(e) {
this.positionLeft += e.movementX
this.positionTop += e.movementY
this.object.style.left = this.clamp(this.positionLeft, 0, this.positionLeftMax) + this.unit
this.object.style.top = this.clamp(this.positionTop, 0, this.positionTopMax) + this.unit
}
handleMouseUp() {
document.removeEventListener('mousemove', this.handleMouseMove)
document.removeEventListener('mouseup', this.handleMouseUp)
}
}
app.js
new Draggable('.draggable-object', '.draggable-subject')
Note
There is one modification that I made since the previous logic.
MouseUp Event on Document
In section Note in the previous article, I pointed out that mouseup event triggered outside browser—more specifically outside the bar area—will cause a draggable element to keep tracking mouse pointer even when the mouse is back in browser window.
This issue can be easily solved by adding mouseup event to document instead of the bar.
This modification also makes a draggable element more responsive to the mouse pointer. The mousemove event on the bar area could not respond to a quick mouse movement which results the mouse pointer goes outside the bar.