skip to Main Content

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


  1. It could be a selection issue caused by the browser. Try user-select: none CSS rule

    Login or Signup to reply.
  2. if I move the mouse very quickly, the mouseup event sometimes fails to register

    The 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);

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search