解决 el-table 在 fixed 状态下获取 dom 不准确的问题

关键词: vue el-table fixed 焦点 dom

近日有需求是 el-table 中焦点跳转,回车跳转到同一列下一行,一列结束之后回到下一列第一行。

如图所示:

开始一切正常,通过使用 :ref="getActualValueInputRef(scope.$index, index)" 可以设置对应的 ref ,并通过 this.$refs\[XXX] 获取对应的 dom

javascript 复制代码
<el-input
  v-if="!readonly"
  :ref="getActualValueInputRef(scope.$index, index)"
  v-model="scope.row.valueActuals[index - 1]"
  :placeholder="getActualValueLabel(index)"
  size="small"
  @keydown.enter.native="handleActualValueEnter(scope.$index, index, $event)"
  @input="handleValueActualChange(scope.row, index - 1, $event)"
/>


// 生成实测值输入框的 ref 名称
getActualValueInputRef(rowIndex, colIndex) {
  return `actualValueInput_${rowIndex}_${colIndex}`
},

// 聚焦到指定的实测值单元格
focusActualValueCell(rowIndex, colIndex) {
    const refName = this.getActualValueInputRef(rowIndex, colIndex)
    const inputComponents = this.$refs[refName]
    const targetComponent = inputComponents[inputComponents.length - 1]
    if (targetComponent) {
      // 直接操作原生 input 元素
      const inputElement = targetComponent.$el?.querySelector('input') || targetComponent.$refs?.input
      if (inputElement) {
        inputElement.focus()
        // 延迟选中,确保焦点已设置
        setTimeout(() => {
          inputElement.select()
        }, 0)
      }
    }
}

但由于 el-table 设置了 fixed 所以导致获取的 inputComponents 是一个数组 ,一开始直接获取数组最后一个元素即可:inputComponents\[inputComponents.length - 1]

但后面新增行的时候发现无法正确获取焦点。调试之后发现焦点跳转到了 fixed 复制的副本元素中。即默认渲染的 inputComponents 中最后一个元素是页面上展示的元素。但新增的行中 inputComponents 的最后一个元素是 fixed 的副本,第一个元素才是页面上展示的元素

解决方案:

javascript 复制代码
// 判断组件是否位于display区域(非fixed区域)
isDisplayAreaComponent(component) {
  if (!component || !component.$el) return false

  // 检查组件是否在非fixed的表格中
  const tableEl = component.$el.closest('.el-table')
  if (!tableEl) return false

  // 检查是否在fixed列中
  const isFixedLeft = component.$el.closest('.el-table__fixed') // 左固定
  const isFixedRight = component.$el.closest('.el-table__fixed-right') // 右固定

  // 如果不在任何fixed区域,则认为是display区域的组件
  return !isFixedLeft && !isFixedRight
},

// 聚焦到指定的实测值单元格
focusActualValueCell(rowIndex, colIndex) {
    const refName = this.getActualValueInputRef(rowIndex, colIndex)
    const inputComponents = this.$refs[refName]
    
    let targetComponent = null
    // 首先尝试找到display区域的组件(非fixed区域)
    for (const component of inputComponents) {
      if (this.isDisplayAreaComponent(component)) {
        targetComponent = component
        break
      }
    }

    // 如果还是没找到,使用最后一个组件作为后备方案
    if (!targetComponent) {
      targetComponent = componentArray[componentArray.length - 1]
    }
    
    if (targetComponent) {
      // 直接操作原生 input 元素
      const inputElement = targetComponent.$el?.querySelector('input') || targetComponent.$refs?.input
      if (inputElement) {
        inputElement.focus()
        // 延迟选中,确保焦点已设置
        setTimeout(() => {
          inputElement.select()
        }, 0)
      }
    }
}

通过遍历 inputComponents 元素使用 isDisplayAreaComponent 来判断该元素是否是 fixed 复制出来的副本,只获取真实的页面的元素并跳转焦点。

至此该问题完全解决。关键是通过 $el.closest() 来判断是否是 fixed 副本。

closest() 是一个 DOM 方法,从当前元素开始,沿 DOM 树向上查找匹配指定选择器的最近的祖先元素(包括元素本身)。例如:

  • element.closest('.el-table') 会从当前元素开始向上查找,返回第一个具有 .el-table 类的祖先元素

通过查找父元素是否存在样式 .el-table__fixed,.el-table__fixed-right 判断该元素是否是 el-table 因为 fixed 创建的副本,从而来找到页面上展示的 dom 。

相关推荐
妙码生花5 分钟前
从 PHP 到 AI + Golang,程序员自救转型手记(十九):点选验证码代码逐行目检
前端·后端·go
Awu12271 小时前
⚡从零开发 Agent CLI(五)实现一个可治理、可扩展的工具系统
前端·人工智能·claude
咪库咪库咪1 小时前
Vue3-生命周期
前端
莪_幻尘2 小时前
你的 AI Skill 越多越蠢?Token 上下文爆炸的求生指南
前端·ai编程
lichenyang4532 小时前
从 has.echo 到异步 API 注册表:一次 ASCF API 回调不触发的排查复盘
前端
林瞅瞅2 小时前
Nuxt3 项目部署 Nginx 防盗链后特定 JS 文件 403 问题修复方案
前端
kyriewen3 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git
一颗奇趣蛋3 小时前
Web 视频开发完全指南:从入门到精通
前端
非洲农业不发达3 小时前
windows终端体验大升级,让你拥有macos级别的美化
前端·后端
妙码生花3 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十七):登录接口完善,登录页接口整合,解决跨域
前端·后端·ai编程