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.draggable = false;
inputWrapper.index = inputs.length++;
inputWrapper.className = `p-1 absolute group cursor-move border flex items-center justify-center`; // add select-none class, making user-select:none **resolves the issue
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>
2
Answers
It could be a selection issue caused by the browser. Try
user-select: none
CSS ruleThe dragable object could not follow the quick movement, add something to your code that would take care of this, i. e.
document.addEventListener("mouseout", dragEnd);