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

最终效果

相关推荐
木子七1 分钟前
Js Dom
前端·javascript
谢小飞34 分钟前
我做了三把椅子原来纹理这样加载切换
前端·three.js
圈圈的熊34 分钟前
HTTP 和 HTTPS 的区别
前端·网络协议·http·https
GIS程序媛—椰子42 分钟前
【Vue 全家桶】2、Vue 组件化编程
前端·javascript·vue.js
Beamon__1 小时前
element-plus按需引入报错IconsResolver is not a function
前端
努力奔波的程序猿1 小时前
HBuilderx修改主题色-改变编辑器背景颜色等
前端
正小安1 小时前
Vue 3 性能提升与 Vue 2 的比较 - 2024最新版前端秋招面试短期突击面试题【100道】
前端·vue.js·面试
yqcoder1 小时前
electron 中 ipcRenderer 的常用方法有哪些?
前端·javascript·electron
T0uken1 小时前
【Python】Bottle:轻量Web框架
开发语言·前端·python