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

react-grid-layout点击元素触发拖拽而非点击事件问题求助

React-Grid-Layout 点击元素触发拖拽而非onClick事件的解决方案

在使用react-grid-layout组件构建包含可点击、可拖拽元素的网格时,遇到点击元素无需移动光标就直接触发拖拽功能,无法触发div上绑定的onClick事件的问题。相关实现代码如下:

Render方法代码

render() {
    return (
        <div className="gridL" style={{minHeight: '65px'}}>
          <StarIcon style={{ position: 'absolute', fontSize: 26, marginTop: 20, marginLeft: '5px', color: 'orange'}} />
          {this.props.favoritesData == null || 
          this.props.favoritesData.length == 0 && 
          (
              <div style={{ position: 'absolute', marginLeft: '35px', marginTop: '24px', height: '30px', color: 'gray'}}>
                Comece a adicionar favoritos para facilitar o acesso aos seus sistemas. 
                <img alt="hehehe" width={10} src="https://rastreio.grupoleonora.com.br/favicon.ico" />
              </div>
          )}
          <ResponsiveReactGridLayout
              {...this.props}
              layouts={this.state.layouts}
              cols={{ xl: 22, lg: 20, md: 16, sm: 12, xs: 8, xxs: 6 }}
              breakpoints={{ xl: 1400, lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
              rowHeight={50}
              isBounded={false}
              onDrag={this.onDrag}
              onDragStop={this.onDragStop}
              onLayoutChange={this.onLayoutChange}
              className="favorites-grid-layout"
              onBreakpointChange={this.onBreakpointChange}
              onDrop={this.onDrop}
              maxRows={1}
              verticalCompact={true}
              measureBeforeMount={false}
              isResizable={false}
              useCSSTransforms={true}
              compactType="horizontal"
              preventCollision={this.state.mounted}
              isDroppable={true}
              isDraggable={true}
              style={{ marginLeft: 25, minHeight: 55 }}
          >
            {Array.isArray(this.state.layouts[this.state.currentBreakpoint])
            ? this.generateDOM(this.state.layouts[this.state.currentBreakpoint])
            : null}
          </ResponsiveReactGridLayout>
        </div>
    );
  }

generateDOM方法代码

generateDOM(layouts: any) 
  {
    return layouts.map((el: any) => {
      const moduleId = this.props.modulesData?.find(q => q.id.toString() === el.i)?.id || -1;
      const isContextMenuOpen = this.state.contextMenus?.find(c => c.index?.toString() === el.i)?.index !== undefined &&
        this.state.contextMenus?.find(c => c.index?.toString() === el.i)?.index !== null;

      return (
        <div
          key={el.i}
          data-grid={el}
          onClick={() => !this.state.isDragging && !isContextMenuOpen && this.openFavorite(this.props.modulesData?.find(q => q.id.toString() === el.i)?.url)}
          onContextMenu={(e: any) => this.handleContextMenu(this.props.modulesData?.find(q => q.id.toString() === el.i)?.id || -1, e)}
          style={{ cursor: 'pointer' }}
        >
          <CardLinkFavoriteDisabled
            image={this.props.modulesData?.find(q => q.id.toString() === el.i)?.imageNavigation.filename}
            title={this.props.modulesData?.find(q => q.id.toString() === el.i)?.name}
          />
          <Menu
            PaperProps={{
              sx: {
                boxShadow: '0px 2px 10px 0px rgba(58, 53, 65, 0.1) !important',
                backgroundImage: 'linear-gradient(98deg, rgba(10,12,71,0.9), rgba(59,240,58,0.9) 160%)'
              },
            }}
            open={isContextMenuOpen}
            onClose={() => this.handleClose(moduleId)}
            anchorReference="anchorPosition"
            anchorPosition={
              this.props.modulesData?.find(q => q.id.toString() === el.i) !== null
                ? { top: this.state.contextMenus?.find(c => c.index === moduleId)?.mouseY || 0, left: this.state.contextMenus?.find(c => c.index === moduleId)?.mouseX || 0 }
                : undefined
            }
            elevation={0}
            componentsProps={{
              root: {
                onContextMenu: (e) => {
                  e.preventDefault();
                  this.handleClose(moduleId);
                },
              },
            }}
          >
            <MenuItem style={{ fontSize: 12, height: 15, color: 'white' }} onClick={(e) => { this.RemoveFavorite(e, moduleId) }}>
              <DeleteIcon color="error" />
              Remover
            </MenuItem>
          </Menu>
        </div>
      );
    });
  }

解决方案

react-grid-layout的拖拽逻辑会监听元素的mousedown事件,默认优先级高于点击事件,可通过以下三种方式解决:

方式1:指定拖拽触发区域

将拖拽触发限制在特定手柄区域,而非整个元素,点击元素其他区域时触发onClick:

  1. 修改generateDOM中的元素结构,添加拖拽手柄:
return (
  <div
    key={el.i}
    data-grid={el}
    onClick={() => !this.state.isDragging && !isContextMenuOpen && this.openFavorite(this.props.modulesData?.find(q => q.id.toString() === el.i)?.url)}
    onContextMenu={(e: any) => this.handleContextMenu(this.props.modulesData?.find(q => q.id.toString() === el.i)?.id || -1, e)}
    style={{ cursor: 'pointer', position: 'relative' }}
  >
    {/* 拖拽手柄区域 */}
    <div 
      className="drag-handle"
      style={{ 
        position: 'absolute', 
        top: 0, 
        right: 0, 
        width: '20px', 
        height: '100%', 
        cursor: 'grab' 
      }}
    />
    <CardLinkFavoriteDisabled
      image={this.props.modulesData?.find(q => q.id.toString() === el.i)?.imageNavigation.filename}
      title={this.props.modulesData?.find(q => q.id.toString() === el.i)?.name}
    />
    {/* 菜单代码保持不变 */}
  </div>
);
  1. 在ResponsiveReactGridLayout组件中添加draggableHandle属性,指定拖拽触发选择器:
<ResponsiveReactGridLayout
  // 其他属性保持不变
  draggableHandle=".drag-handle"
>
  {/* 子元素 */}
</ResponsiveReactGridLayout>

方式2:设置拖拽触发阈值+状态判断

通过组件配置和状态控制,只有鼠标移动超过一定距离才触发拖拽:

  1. 添加拖拽状态控制方法:
onDragStart = () => {
  this.setState({ isDragging: true });
};

onDragStop = () => {
  setTimeout(() => {
    this.setState({ isDragging: false });
  }, 100);
};
  1. 在ResponsiveReactGridLayout中绑定事件并设置拖拽阈值:
<ResponsiveReactGridLayout
  // 其他属性保持不变
  onDragStart={this.onDragStart}
  onDragStop={this.onDragStop}
  dragStartThreshold={3} // 鼠标移动3像素后才触发拖拽
>
  {/* 子元素 */}
</ResponsiveReactGridLayout>

方式3:阻止点击区域事件冒泡

在需要触发点击的子元素上阻止mousedown事件冒泡,避免拖拽逻辑触发:

<CardLinkFavoriteDisabled
  image={this.props.modulesData?.find(q => q.id.toString() === el.i)?.imageNavigation.filename}
  title={this.props.modulesData?.find(q => q.id.toString() === el.i)?.name}
  onMouseDown={(e) => e.stopPropagation()}
/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:38:16