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

最终效果

相关推荐
API_technology18 分钟前
电商API安全防护:JWT令牌与XSS防御实战
前端·安全·xss
yqcoder23 分钟前
Express + MongoDB 实现在筛选时间段中用户名的模糊查询
java·前端·javascript
十八朵郁金香1 小时前
通俗易懂的DOM1级标准介绍
开发语言·前端·javascript
m0_528723812 小时前
HTML中,title和h1标签的区别是什么?
前端·html
Dark_programmer2 小时前
html - - - - - modal弹窗出现时,页面怎么能限制滚动
前端·html
GDAL2 小时前
HTML Canvas clip 深入全面讲解
前端·javascript·canvas
禾苗种树2 小时前
在 Vue 3 中使用 ECharts 制作多 Y 轴折线图时,若希望 **Y 轴颜色自动匹配折线颜色**且无需手动干预,可以通过以下步骤实现:
前端·vue.js·echarts
贵州数擎科技有限公司2 小时前
使用 Three.js 实现流光特效
前端·webgl
JustHappy2 小时前
「我们一起做组件库🌻」做个面包屑🥖,Vue的依赖注入实战💉(VersakitUI开发实录)
前端·javascript·github
祝鹏2 小时前
前端如何制定监控项
前端