Custom Draggable Element With Drag Handles
by Richard Hunter
HTML
<div id="container">
<my-draggable id="alpha">
<div id="foo" class="foo">
<span class="drag-handle">
<my-draggable id="alpha-drag-handle">
<div>drag</div>
</my-draggable>
</span>
</div>
</my-draggable>
<my-draggable id="beta">
<div class="bar"></div>
</my-draggable>
</div>
CSS
.foo {
background: pink;
padding: 10px;
border: solid 3px black;
position: relative;
}
.bar {
background: antiquewhite;
padding: 10px;
width: 40px;
height: 30px;
border: dashed 3px dodgerblue;
}
.drag-handle {
background: red;
display: inline-block;
width: 30px;
height: 20px;
position: absolute;
right: 0;
bottom: 0;
}
#container {
position: relative;
background: green;
height: 100vh;
width: 100vw;
}
body {
margin: 0;
}
JavaScript
let alphaPosition = {
left: 20,
top: 20,
width: 100,
height: 100,
};
let betaPosition = {
left: 50,
top: 50,
};
class Draggable extends HTMLElement {
constructor() {
super();
this.pointerDown = false;
this.style.position = 'absolute';
this.style.left = 0;
this.style.top = 0;
this.addEventListener('pointerdown', (event) => {
event.stopPropagation();
event.target.setPointerCapture(event.pointerId);
this.pointerDown = true;
this.startX = event.clientX;
this.startY = event.clientY;
});
this.addEventListener('pointermove', (event) => {
event.stopPropagation();
if (this.pointerDown) {
var dragEvent = new CustomEvent("drag", {
bubbles: true,
detail: {
id: this.id,
xdiff: event.clientX - this.startX,
ydiff: event.clientY - this.startY,
}
});
this.startX = event.clientX;
this.startY = event.clientY;
event.target.dispatchEvent(dragEvent);
}
});
this.addEventListener('pointerup', (event) => {
event.stopPropagation();
event.target.releasePointerCapture(event.pointerId);
this.pointerDown = false;
});
}
}
customElements.define('my-draggable', Draggable);
function render() {
foo.style.width = alphaPosition.width + 'px';
foo.style.height = alphaPosition.height + 'px';
alpha.style.transform = `translate(${alphaPosition.left}px, ${alphaPosition.top}px)`;
beta.style.transform = `translate(${betaPosition.left}px, ${betaPosition.top}px)`;
}
const leftLimit = 200;
const topLimit = 200;
container.addEventListener('drag', (event) => {
if (event.detail.id === 'alpha') {
alphaPosition = {
...alphaPosition,
left: Math.min(leftLimit, Math.max(20, alphaPosition.left + event.detail.xdiff)),
top: Math.min(topLimit, Math.max(20, alphaPosition.top + event.detail.ydiff)),
};
render();
}
if (event.detail.id ===...