Draggable and resizable boxes
Use height dimensions on wrapper
by Luke Harby
HTML
<div class="outer">
<div class="wrapper">
<div class="box">
I am box 1
</div>
<div class="box second">
I am box 2
</div>
<div class="box second third dragging">
I am box 3
</div>
</div>
</div>
CSS
body {
font-family: monospace;
}
.wrapper {
min-height: 400vh;
background-color: rgb(255 0 0 / 10%);
top: 10px;
right: 10px;
bottom: 10px;
left: 10px;
overflow-y: scroll;
}
.box {
position: absolute;
padding: 10px;
resize: both;
height: 150px;
width: 150px;
margin-bottom: 20px;
background: #f0f0f0;
border: 2px solid #555;
overflow: auto;
background-image: linear-gradient(-45deg, #ccc 10px, transparent 10px);
}
.second {
top: 50px;
left: 50px;
}
.third {
top: 100px;
left: 100px;
}
.heading {
background: red;
padding: 5px 10px;
}
.dragging {
opacity: 0.9;
border: 2px dashed #8181b9;
user-select: none;
z-index: 2;
cursor: move;
}
.actions {
display: flex;
flex-direction: column;
position: absolute;
right: 0;
z-index: 1;
}
JavaScript
const draggingClass = 'dragging';
class draggable {
constructor(constrainHeight, constrainWidth, element, node = [document]) {
this.constrainHeight = constrainHeight;
this.constrainWidth = constrainWidth;
constrainHeight = false;
constrainWidth = false;
this.node = node;
this.el = element;
if (this.el) {
this.mouseDown = this.mouseDown.bind(this);
this.mouseUp = this.mouseUp.bind(this);
this.mouseMove = this.mouseMove.bind(this);
}
}
setHandle(handle) {
this.handle = handle;
return this;
}
setCallback(callback) {
this.callback = callback;
return this;
}
mouseDown(event) {
if (this.callback?.start) {
this.callback.start(event, this.el);
} else {
this.el.classList.add(draggingClass);
}
for (const node of this.node) {
node.addEventListener('mouseup', this.mouseUp);
node.addEventListener('mousemove', this.mouseMove);
node.addEventListener('mouseleave', this.mouseUp);
}
this.el.addEventListener('mouseleave', this.mouseUp);
}
mouseUp(event) {
if (this.callback?.end) {
this.callback.end(event, this.el);
} else {
this.el.classList.remove(draggingClass);
}
for (const node of this.node) {
node.removeEventListener('mouseup', this.mouseUp);
node.removeEventListener('mousemove', this.mouseMove);
node.removeEventListener('mouseleave', this.mouseUp);
}
this.el.removeEventListener('mouseleave', this.mouseUp);
}
mouseMove(event) {
if (this.callback?.move) {
this.callback.move(event, this.el);
} else {
const style = window.getComputedStyle(this.el);
const x = (parseFloat(style.getPropertyValue('left')) || 0) + event.movementX;
...