You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用dnd-kit/sortable时阻止子组件拖动触发父组件拖拽

解决@dnd-kit/sortable子元素拖拽触发父组件拖拽的问题

核心问题原因

@dnd-kit/sortable默认会将拖拽监听绑定到整个列表项容器上,当用户拖动子元素(如textarea)时,事件会冒泡到父容器,触发拖拽逻辑。单纯使用e.preventDefault()或e.stopPropagation()无法直接阻断dnd-kit的拖拽启动流程,需要结合其API进行处理。

解决方案一:使用拖拽手柄(推荐)

只将拖拽触发区域限制在特定的手柄元素上,子元素不会触发拖拽。修改列表项组件,将useSortable返回的attributes和listeners绑定到手柄元素,而非整个容器:

import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

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

  const itemStyle = {
    transform: CSS.Transform.toString(transform),
    transition,
    display: 'flex',
    gap: '12px',
    alignItems: 'center',
    padding: '8px',
    border: '1px solid #eee',
    marginBottom: '8px',
  };

  const handleStyle = {
    cursor: 'grab',
    padding: '4px 8px',
    border: 'none',
    background: '#f5f5f5',
  };

  return (
    <div ref={setNodeRef} style={itemStyle}>
      {/* 拖拽手柄:只有点击/拖动这个元素才会触发排序 */}
      <button {...attributes} {...listeners} style={handleStyle}>
        ☰
      </button>
      <textarea
        style={{ flex: 1, minHeight: '60px' }}
        placeholder="拖动我不会触发父组件拖拽"
      />
    </div>
  );
};

解决方案二:在子元素上主动取消拖拽

如果希望保留整个列表项的拖拽能力,但排除特定子元素,可以在子元素的onPointerDown事件中调用useSortable提供的cancel方法,主动终止拖拽启动:

import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

const SortableItem = ({ id }) => {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    cancel, // 引入cancel方法
  } = useSortable({ id });

  const itemStyle = {
    transform: CSS.Transform.toString(transform),
    transition,
    padding: '8px',
    border: '1px solid #eee',
    marginBottom: '8px',
  };

  const handleTextareaPointerDown = (e) => {
    // 主动取消dnd-kit的拖拽流程
    cancel();
    // 允许textarea的默认交互(如选中文本、调整大小)
    e.stopPropagation();
  };

  return (
    <div ref={setNodeRef} style={itemStyle} {...attributes} {...listeners}>
      <textarea
        style={{ width: '100%', minHeight: '60px' }}
        onPointerDown={handleTextareaPointerDown}
        placeholder="拖动我不会触发父组件拖拽"
      />
    </div>
  );
};

注意事项

  • 使用onPointerDown而非onMouseDown:dnd-kit默认基于Pointer事件实现拖拽逻辑,因此在PointerDown阶段取消拖拽才能生效。
  • 方案一的优势更明显:不仅避免了子元素触发拖拽的问题,还能明确告知用户拖拽触发区域,提升交互体验。

内容的提问来源于stack exchange,提问作者PhoenixDown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:54:50