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'
       }
     }

最终效果

相关推荐
无人机90112 分钟前
Delphi 网络编程实战:TIdTCPClient 与 TIdTCPServer 类深度解析
java·开发语言·前端
lUie INGA1 小时前
rust web框架actix和axum比较
前端·人工智能·rust
OPHKVPS2 小时前
VoidStealer新型窃密攻击:首例利用硬件断点绕过Chrome ABE防护,精准窃取v20_master_key
前端·chrome
gechunlian882 小时前
SpringBoot3+Springdoc:v3api-docs可以访问,html无法访问的解决方法
前端·html
驾驭人生2 小时前
ASP.NET Core 实现 SSE 服务器推送|生产级实战教程(含跨域 / Nginx / 前端完整代码)
服务器·前端·nginx
酉鬼女又兒3 小时前
零基础快速入门前端ES6 核心特性详解:Set 数据结构与对象增强写法(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·职场和发展·蓝桥杯·es6
慧一居士3 小时前
Vue项目中,子组件调用父组件方法示例,以及如何传值示例,对比使用插槽和不使用插槽区别
前端·vue.js
我是伪码农3 小时前
HTML和CSS复习
前端·css·html
林恒smileZAZ3 小时前
前端实现进度条
前端
前端老石人3 小时前
邂逅前端开发:从基础到实践的全景指南
开发语言·前端·html