OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Prevent drag when user drags a child component of a draggable component using dnd-kit sortable

  • Thread starter Thread starter PhoenixDown
  • Start date Start date
P

PhoenixDown

Guest
I'm using dnd-kit/sortable to allow sorting a list of items using drag-and-drop. This works fine except when users drag a textarea or another child element the entire component is dragged. This becomes a problem when highlight text for example. What I'd like to do is prevent drag action when a user drags a child component of a draggable parent.

I tried using e.preventDefault() and e.stopPropagation(), but neither of those stop the dragging. I've added my code to this sandbox - https://codesandbox.io/p/sandbox/xenodochial-villani-783hjz

In case there is an issue with the sandbox this is the code:

SortableItem.js

Code:
import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

const SortableItem = ({ id, image, text }) => {
  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  const stopPropagation = (e) => {
    e.preventDefault();
    e.stopPropagation();
  };

  const update = (e) => {
    alert(1);
  };

  return (
    <div
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className="sortable-item"
    >
      <img
        width="140px"
        src="https://img.freepik.com/free-psd/3d-illustration-human-avatar-profile_23-2150671142.jpg?w=826&t=st=1719155268~exp=1719155868~hmac=af598917d4ff9f549cf54d8d4f1f6e2305b114f067bac23cc6f5d63e2f57b7e8"
        alt=""
      />
      <textarea
        onMouseDown={stopPropagation}
        onTouchStart={stopPropagation}
        onDragStart={stopPropagation}
        onDrop={stopPropagation}
      >
        {text}
      </textarea>
      <button onClick={update}>Update</button>
    </div>
  );
};

export default SortableItem;

SortableList.js

Code:
import React, { useState } from "react";
import {
  DndContext,
  useSensor,
  PointerSensor,
  MouseSensor,
  TouchSensor,
  KeyboardSensor,
  useSensors,
} from "@dnd-kit/core";
import {
  SortableContext,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { arrayMove } from "@dnd-kit/sortable";
import SortableItem from "./SortableItem";

const SortableList = () => {
  const pointerSensor = useSensor(PointerSensor, {
    activationConstraint: {
      distance: 8,
    },
  });
  const mouseSensor = useSensor(MouseSensor);
  const touchSensor = useSensor(TouchSensor);
  const keyboardSensor = useSensor(KeyboardSensor);

  const sensors = useSensors(
    mouseSensor,
    touchSensor,
    keyboardSensor,
    pointerSensor
  );
  const initialItems = [
    { id: 1, image: "image1.jpg", text: "Item 1" },
    { id: 2, image: "image2.jpg", text: "Item 2" },
    { id: 3, image: "image3.jpg", text: "Item 3" },
    { id: 4, image: "image4.jpg", text: "Item 4" },
  ];

  const [items, setItems] = useState(initialItems);

  const handleDragEnd = (event) => {
    const { active, over } = event;

    if (active.id !== over.id) {
      setItems((items) => {
        const oldIndex = items.findIndex((item) => item.id === active.id);
        const newIndex = items.findIndex((item) => item.id === over.id);
        return arrayMove(items, oldIndex, newIndex);
      });
    }
  };

  return (
    <DndContext onDragEnd={handleDragEnd} sensors={sensors}>
      <SortableContext
        items={items.map((item) => item.id)}
        strategy={verticalListSortingStrategy}
      >
        <div className="sortable-list">
          {items.map((item) => (
            <SortableItem
              key={item.id}
              id={item.id}
              image={item.image}
              text={item.text}
            />
          ))}
        </div>
      </SortableContext>
    </DndContext>
  );
};

export default SortableList;

App.js

Code:
import "./styles.css";
import SortableList from "./SortableList";

export default function App() {
  return (
    <div className="App">
      <h1>Sortable List</h1>
      <SortableList />
    </div>
  );
}

<p>I'm using dnd-kit/sortable to allow sorting a list of items using drag-and-drop. This works fine except when users drag a textarea or another child element the entire component is dragged. This becomes a problem when highlight text for example. What I'd like to do is prevent drag action when a user drags a child component of a draggable parent.</p>
<p>I tried using e.preventDefault() and e.stopPropagation(), but neither of those stop the dragging. I've added my code to this sandbox - <a href="https://codesandbox.io/p/sandbox/xenodochial-villani-783hjz" rel="nofollow noreferrer">https://codesandbox.io/p/sandbox/xenodochial-villani-783hjz</a></p>
<p>In case there is an issue with the sandbox this is the code:</p>
<p>SortableItem.js</p>
<pre><code>import React from "react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

const SortableItem = ({ id, image, text }) => {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id });

const style = {
transform: CSS.Transform.toString(transform),
transition,
};

const stopPropagation = (e) => {
e.preventDefault();
e.stopPropagation();
};

const update = (e) => {
alert(1);
};

return (
<div
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
className="sortable-item"
>
<img
width="140px"
src="https://img.freepik.com/free-psd/3d...54d8d4f1f6e2305b114f067bac23cc6f5d63e2f57b7e8"
alt=""
/>
<textarea
onMouseDown={stopPropagation}
onTouchStart={stopPropagation}
onDragStart={stopPropagation}
onDrop={stopPropagation}
>
{text}
</textarea>
<button onClick={update}>Update</button>
</div>
);
};

export default SortableItem;
</code></pre>
<p>SortableList.js</p>
<pre><code>import React, { useState } from "react";
import {
DndContext,
useSensor,
PointerSensor,
MouseSensor,
TouchSensor,
KeyboardSensor,
useSensors,
} from "@dnd-kit/core";
import {
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { arrayMove } from "@dnd-kit/sortable";
import SortableItem from "./SortableItem";

const SortableList = () => {
const pointerSensor = useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
});
const mouseSensor = useSensor(MouseSensor);
const touchSensor = useSensor(TouchSensor);
const keyboardSensor = useSensor(KeyboardSensor);

const sensors = useSensors(
mouseSensor,
touchSensor,
keyboardSensor,
pointerSensor
);
const initialItems = [
{ id: 1, image: "image1.jpg", text: "Item 1" },
{ id: 2, image: "image2.jpg", text: "Item 2" },
{ id: 3, image: "image3.jpg", text: "Item 3" },
{ id: 4, image: "image4.jpg", text: "Item 4" },
];

const [items, setItems] = useState(initialItems);

const handleDragEnd = (event) => {
const { active, over } = event;

if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over.id);
return arrayMove(items, oldIndex, newIndex);
});
}
};

return (
<DndContext onDragEnd={handleDragEnd} sensors={sensors}>
<SortableContext
items={items.map((item) => item.id)}
strategy={verticalListSortingStrategy}
>
<div className="sortable-list">
{items.map((item) => (
<SortableItem
key={item.id}
id={item.id}
image={item.image}
text={item.text}
/>
))}
</div>
</SortableContext>
</DndContext>
);
};

export default SortableList;
</code></pre>
<p>App.js</p>
<pre><code>import "./styles.css";
import SortableList from "./SortableList";

export default function App() {
return (
<div className="App">
<h1>Sortable List</h1>
<SortableList />
</div>
);
}
</code></pre>
 

Latest posts

Top