使用 Element UI 中 el-dropdown 时避免的一个常见坑:嵌套 el-button 引发的点击失效

548 阅读2分钟

在使用 Element UI(或 Element Plus)进行后台表格操作按钮开发时,我们通常会将部分“更多操作”放入 <el-dropdown> 中,例如“编辑”、“删除”等。

这时候,很多人会不自觉地在 <el-dropdown-item> 里包裹一个 <el-button>,但这种做法却极容易导致点击不生效,或者事件穿透问题。

本文将通过一个实际示例说明该问题,并给出推荐做法。


❌ 问题代码:<el-dropdown-item> 中嵌套了 <el-button>

<el-button 
  size="mini" 
  type="text" 
  @click="handleClick(scope.row.guid)" 
> 
  查看 
</el-button> 

<el-button 
  v-if="scope.row.origin !== '资产指标'" 
  size="mini" 
  type="text" 
  @click="handleCopyClick(scope.row)" 
> 
  复制 
</el-button>

<el-dropdown-menu slot="dropdown">
  <el-dropdown-item command="edit">
    <el-button
      type="text"
      size="small"
      :disabled="scope.row.quotaStatus==='校验中'"
      @click="handleEditClick(scope.row)"
    >
      编辑
    </el-button>
  </el-dropdown-item>
  <el-dropdown-item command="delete">
    <el-button
      size="mini"
      type="text"
      @click="handleDeleteClick(scope.row.guid)"
    >
      删除
    </el-button>
  </el-dropdown-item>
</el-dropdown-menu>

⚠️ 这样做会出现什么问题?

  • 有时候点击没反应
  • 在部分浏览器或条件下“点击穿透”
  • 有些事件会失效,或者触发两次

🔍 原因分析:DOM 结构不稳定 + 事件委托冲突

Element UI 的 <el-dropdown-item> 是通过事件委托(event delegation)来监听点击事件的。

当你在里面再嵌套 <el-button>,这个按钮自身会注册事件处理器,可能会阻止冒泡或捕获,从而干扰了 <el-dropdown-item> 的事件机制。


✅ 推荐做法一:使用 @click.native 绑定事件

<el-dropdown-item
  v-if="getEditFlag(scope.row).showFlag"
  :disabled="getEditFlag(scope.row).disableFlag"
  @click.native="handleEditClick(scope.row)"
>
  编辑
</el-dropdown-item>

<el-dropdown-item
  v-if="getDeleteFlag(scope.row).showFlag"
  :disabled="getDeleteFlag(scope.row).disableFlag"
  @click.native="handleDeleteClick(scope.row.guid)"
>
  删除
</el-dropdown-item>

✅ 好处:

  • 减少嵌套结构,DOM 更稳定
  • 避免点击事件失效
  • 写法简洁、清晰

✅ 推荐做法二:使用 command 属性统一处理

Element UI/Plus 推荐使用 command 来处理 <el-dropdown-item> 的点击事件。你只需要监听一个 @command,再根据传入的参数判断要执行的操作。

重构后的表格操作列代码如下:

<el-table-column label="操作" width="180">
  <template slot-scope="scope">
    <el-button
      size="mini"
      type="text"
      @click="handleClick(scope.row.guid)"
    >
      查看
    </el-button>

    <el-button
      v-if="scope.row.origin !== '资产指标'"
      size="mini"
      type="text"
      @click="handleCopyClick(scope.row)"
    >
      复制
    </el-button>

    <el-dropdown
      v-if="scope.row.allowChange || superAdmin"
      placement="bottom"
      @command="handleCommand"
    >
      <span style="cursor: pointer; margin-left: 10px; color: #409eff; font-size: 12px;">
        <i class="el-icon-more" style="transform: rotate(90deg); color: #409eff;"></i>
      </span>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item
          :command="{ type: 'edit', row: scope.row }"
          :disabled="scope.row.quotaStatus === '校验中'"
        >
          编辑
        </el-dropdown-item>
        <el-dropdown-item
          :command="{ type: 'delete', guid: scope.row.guid }"
        >
          删除
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </template>
</el-table-column>

在 methods 中统一处理逻辑:

methods: {
  handleCommand(cmd) {
    switch (cmd.type) {
      case 'edit':
        this.handleEditClick(cmd.row);
        break;
      case 'delete':
        this.handleDeleteClick(cmd.guid);
        break;
    }
  }
}

🔧 补充说明

  • command 可以是字符串,也可以是对象 —— 更推荐传对象,更灵活。
  • 不要再用 @click 放在 <el-button> 里面了,会带来 DOM 嵌套、事件穿透等问题。

✅ 总结

做法是否推荐原因说明
el-dropdown-item 包 el-button❌ 不推荐会导致点击失效
使用 @click.native 在 item 上✅ 推荐简洁直接,事件绑定稳定
使用 command 统一处理✅ 强烈推荐解耦清晰、维护方便