Element plus 跟随页面的横向滚动条及固定表头

问题

  • 原生的el-table组件的固定表头必须要设置高度,当表格高度需要动态变化的时候该选项无效。
  • 固定列之后出现横向滚动条,但该滚动条也无法跟随页面移动只能固定在表格底部,当数据过多时,无法直接操作。

分析

目前滚动条的解决方案是两种:

  1. 手搓滚动条,过于繁琐了
  2. 第三方组件,目前没找到可以用的

固定表头的方案是动态的计算max-height,理论是可以的,但在我的项目无效

解决方案

滚动条

我的整体思想是在不破坏原有滚动条的情况下,更改它的定位来实现随页面滚动

它的滚动条的定位是当通过position:absolute 定位到表格底部,以及display来控制显示与否。

属性scrollbar-always-on可以来解决不显示的问题。

定位的解决方案是:切换absolute和fixed定位来控制滚动条的位置。当超过表格的高度或者表格不可见时,使其保持他原生的定位,反之则固定在页面底部。

由于滚动条并不是出现在表格内部,所以是给它的外部容器添加滚动事件

其中elScrollBarH是滚动条的外部容器,并不是滚动条本身 ,tableContent则是表格的内容区域,用于给滚动条定位

ts 复制代码
  /**
        * @description: 设置滚动条的位置
        * 这里调整的是滚动条的包裹容器,他相对于表格容器来定位,
        * 而内部的实际的滚动条相对于这个容器使用tranlate来调整位置
        * 所以在调回的时候,直接把left设置为0,仍然能保持滚动条的相对位置
        * @param {*} state true:固定在可视区域,false:固定在表格容器内
        * @return {*}
        */
       const setScrollPos = (state: boolean) => {
         if (state) {
           if (!elScrollBarH.value || !tableContent.value) return
     ​
           elScrollBarH.value.style.position = 'fixed'
           // 这里去找表格的content区域,把他相对于视口的left值设置给滚动条的容器
           elScrollBarH.value.style.left =
             tableContent.value?.getBoundingClientRect().left + 'px'
         } else {
           elScrollBarH.value!.style.left = 0 + 'px'
           elScrollBarH.value!.style.position = 'absolute'
         }
       }

重点在于state条件的判断,为true的情况有只有一种,表格出现在视口内且滚动高度没有超过表格所在高度 。为此,需要两个部分的判定,首先是滚动的时候需要对高度进行判断 ,第二需要一个可见性判断

ts 复制代码
       /**
        * @description: 判定当前横向滑动条是否在表格内
        * 滑动距离加可见区域高度不大于表格总高度,则说明在表格内
        * @return {*}
        */
       const isIntable = () => {
         if (!tableContent.value || !tableContainer.value) return false
         const { scrollTop, offsetHeight, scrollHeight } =
           tableContainer.value as HTMLElement
         let result = scrollTop + offsetHeight < scrollHeight
         isFixed.value = result // 这个一会儿解释
         return result
       }

<!---->

       /**
        * @description: 观察表格是否在可视区域,设置滚动条的对应位置
        * @param {*} entries 观察实例,包含当前观察的元素的状态
        * @return {*}
        */
       const obScroll = (entries: IntersectionObserverEntry[]) => {
         setScrollPos(entries[0].intersectionRatio > 0)  // 这里他是一个数组,但我只有一个滚动条所以就直接访问0了
       }
     ​
       // 表格可见性的观察者
       const tableVisOb = new IntersectionObserver(obScroll)

到这里已经可以正常使用滚动条了,但是当表格宽度发生变化的时候,滚动条并不会跟随表格调整位置,所以还需要对表格尺寸变化进行监视

ts 复制代码
     // 当前滚动条是否固定状态,这个主要用于在给sizeOb使用
     // 如果是固定状态,那么在表格大小改变的时候,left需要重置到表格的最左侧的left位置
     // 如果不是,那么设为0即可
     // 这也是上方出现这个变量的原因
     const isFixed = ref<boolean>(true)
     ​
     /**
      * @description: 生成一个表格大小的观察者,当大小变化去动态的调整滚动条的位置
      * @param {ResizeObserverEntry[]} entries 观察的实例
      * @return {*}
      */
     const tableSizeOb = new ResizeObserver((entries: ResizeObserverEntry[]) => {
       if (!elScrollBarH.value || !tableContainer.value) return
       // 找到表格容器
       let container = entries.find(item => item.target === tableContainer.value)
       if (!container) return
     ​
       // 看当前的固定状态,分别去调整滚动条的位置
       if (isFixed.value) {
         let left = container.target.getBoundingClientRect().left
         elScrollBarH.value.style.left = left + 'px'
       } else {
         elScrollBarH.value.style.left = 0 + 'px'
       }
     })

最后记得挂载

     onMounted(() => {
       tableVisOb.observe(tableContent.value as HTMLElement)
       tableSizeOb.observe(tableContainer.value as HTMLElement)
     })

表头

表头的解决方案就很简单粗暴了,直接计算滑动高度来固定表头及还原表头位置,这个方案会让表头样式有一点点问题,可以调整(但我没调)

ts 复制代码
     if (tableHeaderRef.value) {
       const { scrollTop } = tableContainer.value as HTMLElement
       let contentOffsetTop = tableContent.value!.offsetTop // 父容器距离顶部的高度
       let contentScollHeight = tableContent.value!.scrollHeight // 整个父容器的高度
       // 超出表头原本位置时且小于整个表的高度,则固定表头
       if (
         scrollTop >= contentOffsetTop &&
         scrollTop <= contentOffsetTop + contentScollHeight
       ) {
         tableHeaderRef.value.style.position = 'fixed'
         tableHeaderRef.value.style.top = '125px' // 这里位置是随便给的,可以通过计算顶部导航高度来确定准确的值
         tableHeaderRef.value.style.zIndex = '666'
       } else {
         // 还原
         // 不设置top是因为在static下,top无效
         tableHeaderRef.value.style.position = 'static'
         tableHeaderRef.value.style.zIndex = '1'
       }
     }

最终效果

相关推荐
哈罗哈皮几秒前
龙虾(openclaw)本地快速安装及使用教程
前端·aigc·ai编程
用户23115444530581 分钟前
React中实现“双向绑定”效果的几种方式
前端
HelloReader2 分钟前
Flutter Sliver 高级滚动打造 iOS 通讯录体验(十三)
前端
a11177634 分钟前
程序化几何背景生成器(html 开源)
前端·开源·html
浮笙若有梦1 小时前
我开源了一个比 Ant Design Table 更好用的高性能虚拟表格
前端·vue.js
一只程序熊1 小时前
vite-cool-unix-ctx] Unexpected token l in JSON at position 0
java·服务器·前端
张元清1 小时前
React Hooks vs Vue Composables:2026 年全面对比
前端·javascript·面试
yuki_uix1 小时前
从三个自定义 Hook 看 React 状态管理的设计思想
前端·javascript
大漠_w3cpluscom1 小时前
如何在 clamp() 中使用 auto 值
前端·css·html
Younglina1 小时前
🏸 从零打造一个羽毛球球线追踪网站:纯前端实战指南
前端