使用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
相关产品推荐
相关产品推荐

