I am trying to create a draggable input field. On click of ‘Create input’ button, an input is created and a mousedown event is registered on the created input. And dragStart is fired, which further register mouseup and mousemove, which handles the drag functionality.
Most of the time, the dragging functionality works properly, but if I move the mouse very quickly, the mouseup event sometimes fails to register. I am unsure what might be causing this issue.
Thank you in advance for your insights!
const inputs = [];
let selectedInput = null;
let bufferX = null;
let bufferY = null;
let isDragging = false;
const workAreaLeftMargin = parseInt(
getComputedStyle(document.querySelector("#work-area")).marginLeft
);
const dragStart = (e) => {
// Prevent unwanted propagation
e.stopPropagation();
// Initialize dragging state
selectedInput = e.target;
if (!selectedInput) return;
const rect = selectedInput.getBoundingClientRect();
bufferX = e.clientX - rect.left;
bufferY = e.clientY - rect.top;
isDragging = true;
// Add listeners for drag lifecycle
document.addEventListener("mousemove", dragging);
document.addEventListener("mouseup", dragEnd);
console.log("Drag started");
};
const dragging = (e) => {
if (!isDragging || !selectedInput) return;
// Update position
const newTop = e.clientY - bufferY;
const newLeft = e.clientX - bufferX - workAreaLeftMargin;
selectedInput.style.top = `${newTop}px`;
selectedInput.style.left = `${newLeft}px`;
};
const dragEnd = (e) => {
if (!isDragging) return;
// Clean up event listeners
document.removeEventListener("mousemove", dragging);
document.removeEventListener("mouseup", dragEnd);
// Reset state
selectedInput = null;
isDragging = false;
console.log("Drag ended");
};
function createText() {
const workArea = document.querySelector("#work-area");
// Create the draggable wrapper
const inputWrapper = createElement("div");
inputWrapper.index = inputs.length++;
inputWrapper.className = `p-1 absolute group cursor-move border flex items-center justify-center`;
inputWrapper.style.top = `${inputWrapper.index * 5}px`;
inputWrapper.style.left = `${inputWrapper.index * 5}px`;
inputWrapper.innerHTML = `
<div class="relative flex items-center justify-center pointer-events-none">
<div
class="border p-2 rounded-[6px] bg-white text-[10px] absolute h-full group-hover:opacity-50 opacity-0 duration-200 select-none pointer-events-none"
>
<p class="pointer-events-none">X : 0</p>
<p class="pointer-events-none">Y : 0</p>
</div>
<input
class="placeholder-white text-white caret-white bg-transparent text-center p-[10px] border-[2px] border-white focus:border-white focus:outline-none rounded-[6px] max-w-[48px] w-full min-w-[16px] pointer-events-none"
/>
</div>
`;
// Attach `mousedown` to initiate drag
inputWrapper.addEventListener("mousedown", dragStart);
// Append to the work area
workArea.appendChild(inputWrapper);
}
// Utility to create DOM elements
const createElement = (name) => document.createElement(name);
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="./index.js" defer></script>
</head>
<body>
<div id="container" class="bg-red-500 w-screen h-screen flex flex-col">
<!-- screen -->
<div id="screen" class="flex-1 bg-slate-200 relative">
<div
id="work-area"
class="h-full m-auto max-w-[700px] bg-blue-500 p-2 relative"
></div>
</div>
<!-- options -->
<div id="options" class="flex justify-center items-center">
<button
id="create-text"
type="button"
class="bg-blue-500 p-2 rounded m-2 text-white"
onclick="createText()"
>
Create input
</button>
<div id="resize-text-btn" class="flex items-center justify-center">
<button
class="bg-green-200 w-[24px] h-[24px] flex items-center justify-center p-2 rounded-l"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<!--!Font Awesome Free 6.7.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
/>
</svg>
</button>
<p class="bg-white h-[24px] leading-[22px]">14px</p>
<button
class="bg-red-200 w-[24px] h-[24px] flex items-center justify-center p-2 rounded-r"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<!--!Font Awesome Free 6.7.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M432 256c0 17.7-14.3 32-32 32L48 288c-17.7 0-32-14.3-32-32s14.3-32 32-32l352 0c17.7 0 32 14.3 32 32z"
/>
</svg>
</button>
</div>
</div>
</div>
</body>
</html>
I