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

React中使用beautiful-dnd时双击选中文本失效,如何兼顾拖拽与选中文本?

解决方案

问题出在两个核心点:

  • 样式里硬设置了userSelect: "none",直接禁止了文本选中行为
  • 把dragHandleProps绑定在了整个文本容器上,导致整个区域都被标记为拖拽触发区,鼠标事件被拖拽逻辑拦截

下面是两种可行的修复方式:

方案一:使用独立拖拽手柄(推荐)

将拖拽触发区域和文本区域分离,只给特定元素绑定拖拽手柄,文本区域保留默认的选中交互:

const getItemStyle = (isDragging, draggableStyle) => ({
  margin: `0 0 ${grid}px 0`,
  padding: "24px 16px 24px 16px",
  background: isDragging ? "lightgreen" : "unset",
  display: "flex",
  alignItems: "center",
  gap: "16px",
  ...draggableStyle,
});

const getListStyle = (isDraggingOver) => ({
  background: isDraggingOver ? "lightblue" : "#f9f9f9",
  padding: grid,
  width: "100%",
});

<DragDropContext onDragEnd={onDragEnd}>
  <Droppable droppableId="droppable">
    {(provided, snapshot) => (
      <div
        {...provided.droppableProps}
        ref={provided.innerRef}
        style={getListStyle(snapshot.isDraggingOver)}
      >
        {items.map((item, index) => (
          <Draggable
            key={item.draggableId}
            draggableId={item.draggableId}
            index={index}
          >
            {(provided, snapshot) => (
              <div
                key={item.id}
                ref={provided.innerRef}
                {...provided.draggableProps}
                style={getItemStyle(
                  snapshot.isDragging,
                  provided.draggableProps.style
                )}
              >
                {/* 单独的拖拽手柄 */}
                <div {...provided.dragHandleProps} style={{cursor: "grab"}}>
                  ☰
                </div>
                {/* 文本区域,默认支持选中 */}
                <div style={{flex: 1, userSelect: "text"}}>
                  This is my text
                </div>
              </div>
            )}
          </Draggable>
        ))}
        {provided.placeholder}
      </div>
    )}
  </Droppable>
</DragDropContext>

方案二:保留整个区域可拖拽,修复双击选中文本

如果需要整个区域都能触发拖拽,同时支持双击选中文本,可以通过样式调整和事件处理实现:

const getItemStyle = (isDragging, draggableStyle) => ({
  margin: `0 0 ${grid}px 0`,
  padding: "24px 16px 24px 16px",
  background: isDragging ? "lightgreen" : "unset",
  // 仅在拖拽时禁止选中,平时允许文本选择
  userSelect: isDragging ? "none" : "text",
  ...draggableStyle,
});

const getListStyle = (isDraggingOver) => ({
  background: isDraggingOver ? "lightblue" : "#f9f9f9",
  padding: grid,
  width: "100%",
});

const handleDoubleClick = (e) => {
  // 阻止拖拽逻辑拦截双击事件
  e.stopPropagation();
  // 手动选中文本内容
  const selection = window.getSelection();
  const range = document.createRange();
  range.selectNodeContents(e.currentTarget);
  selection.removeAllRanges();
  selection.addRange(range);
};

<DragDropContext onDragEnd={onDragEnd}>
  <Droppable droppableId="droppable">
    {(provided, snapshot) => (
      <div
        {...provided.droppableProps}
        ref={provided.innerRef}
        style={getListStyle(snapshot.isDraggingOver)}
      >
        {items.map((item, index) => (
          <Draggable
            key={item.draggableId}
            draggableId={item.draggableId}
            index={index}
          >
            {(provided, snapshot) => (
              <div
                key={item.id}
                ref={provided.innerRef}
                {...provided.draggableProps}
                {...provided.dragHandleProps}
                style={getItemStyle(
                  snapshot.isDragging,
                  provided.draggableProps.style
                )}
                onDoubleClick={handleDoubleClick}
              >
                This is my text
              </div>
            )}
          </Draggable>
        ))}
        {provided.placeholder}
      </div>
    )}
  </Droppable>
</DragDropContext>

关键说明

  • 方案一的用户体验更清晰,能明确区分拖拽触发区和文本操作区,减少误操作
  • 方案二中,userSelect的条件设置避免了拖拽时意外选中文本,双击时通过stopPropagation阻断拖拽逻辑的干扰,再手动完成文本选中

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:42:43