1

I am trying to create a drag and drop feature that is a child component within a modal where the currently dragged item follows the position of the mouse, however, the position of the mouse is being offset by other content in the modal not part of the drag and drop component. Here is a repl showing the issue I would like for the element to follow the cursor exactly.

Showing the relation between cursor position and modal content

The basic element structure is as follows:

<Main>
  <Modal>
    <h2>Modal Header</h2>

    <DndComponent>
      <div>Drag Me</div>
      <div>Element that follows cursor</div>
    </DndComponent>

  </Modal>
</Main>

I have tried different combinations of e.clientY, e.offsetY, and e.pageY in combination with getBoundingClientRect from the component container. I would prefer to not put a ref on the modal as the drag and drop feature will be used in many different places. I have also tried using both position: absolute and position: fixed

1 Answer 1

2

Consider applying position: absolute to the dragitem. This ensures the "start" position of the element is relative to the boundsRef element, which is where the mousePos is calculated relative to.

const { useState, useRef } = React;

function App() {
  const [isDragging, setIsDragging] = useState(false);
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
  const boundsRef = useRef(null);

  const handleMouseDown = () => {
    setIsDragging(true);
  };

  const handleMouseMove = (e) => {
    const bounds = boundsRef.current.getBoundingClientRect();
    if (isDragging) {
      setMousePos({ x: e.clientX - bounds.x, y: e.clientY - bounds.y });
    }
  };

  return (
    <main>
      <div className="container" onPointerMove={handleMouseMove}>
        <div className="modal">
          <h3>Modal Header</h3>
          <h4 style={{ marginBottom: "20px" }}>Sub Header</h4>

          {/* start of child component */}
          <div ref={boundsRef} style={{ position: "relative" }}>
            <ul>
              <li className="modal-content" onPointerDown={handleMouseDown}>
                <p>Drag Me</p>
              </li>
            </ul>

            {isDragging && (
              <div
                className="dragItem"
                style={{
                  transform: `translate(${mousePos.x}px, ${mousePos.y}px)`,
                }}
              >
                <p>Item Being Dragged</p>
              </div>
            )}
          </div>
          {/* end of child component */}
        </div>
      </div>
    </main>
  );
}

ReactDOM.createRoot(document.getElementById("app")).render(<App />);
* {
  margin: 0;
  padding: 0;
}

.container {
  position: relative;
  height: 100vh;
  width: 100vw;
  background-color:#fff;
}

.modal {
  height: 50vh;
  width: 50vw;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #ededed;
}

.modal-content {
  user-select: none;
}

.modal-content:hover {
  cursor: grab;
}

.dragItem {
  position: absolute;
  z-index: 1000;
  left: 0;
  top: 0;
  pointer-events: none;
  background-color: #8593ff24;
  padding: 0.5rem 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div id="app"></div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.