elementui el-table 横向滚动条保持在可视范围中

实现效果

在这里插入图片描述
当列表内容部分可见时,保持横向滚动条在下方。

实现

原理

这里涉及到两个Dom元素,类名分别为 el-table__body-wrapperel-table__body
通过观察发现横向滚动条在于el-table__body-wrapper上,el-table__body则是实际的列表内容,当el-table__body宽度超出el-table__body-wrapper时就会出现横向滚动条。
因此只需要动态的修改el-table__body-wrapperheight即可实现想要的效果。
在页面发生滚动、页面大小改变以及数据源更新的时候对el-table__body-wrapperheight进行调整。

代码

这里是通过mixin方式的实现

let el;
let tableBodyWrapDom;
let tableBodyDom;

function handle() {
  if (!el) return;
  // top为dom上侧距离可视窗口顶部的值
  const { top: tableBodyDomTop } = tableBodyWrapDom.getBoundingClientRect();  
  if (tableBodyDomTop > window.innerHeight || tableBodyWrapDom.classList.contains('is-scrolling-none')) {
    // 此时列表在可视窗口的下侧不可见区域,因此不做任何修改
    tableBodyWrapDom.style.height = 'unset'
    tableBodyWrapDom.style.marginBottom = 'unset';
  } else {
    // 窗口高度 - 列表距顶部值 且 不超过自身实际值
    let wrapHeight = Math.min(window.innerHeight - tableBodyDomTop, tableBodyDom.offsetHeight);
    tableBodyWrapDom.style.height = wrapHeight + 'px';
    // 需要用marginBottom填充,以保持列表原有高度,避免页面的纵向滚动条变化导致页面滚动的不流畅
    // 可以通过注释这一行代码观察其效果差异
    tableBodyWrapDom.style.marginBottom = (tableBodyDom.offsetHeight - wrapHeight) + 'px';
  }
}

export default {
  mounted() {
 	el = this.$el; // 当前组件的Dom对象,避免操作到本组件之外的Dom
    tableBodyWrapDom = el.querySelector('.el-table__body-wrapper');
    tableBodyDom = el.querySelector('.el-table__body');
    // 监听事件
    document.addEventListener('scroll', handle);
    window.addEventListener('resize', handle);
  },
  destroyed() {
  	// 在组件销毁时取消监听
    document.removeEventListener('scroll', handle);
    window.removeEventListener('resize', handle);
  },
  watch: {
    _list() {
      // 当列表数据源发生变化时,再次触发
      this.$nextTick(handle);
    }
  }
}

2021-2-25 更新 - 一个bug的解决

问题

当鼠标悬停在固定列上滚动滚轮时,会导致左侧内容发生滚动。

原因

查了许久,最终阅读了 el-table 源码发现了一段代码。

这个是固定列html模板的部分代码,主要是这个 =》 v-mousewheel="handleFixedMousewheel"

 <div
      v-if="rightFixedColumns.length > 0"
      v-mousewheel="handleFixedMousewheel"
      class="el-table__fixed-right"
      ref="rightFixedWrapper"
      :style="[{
        width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '',
        right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : ''
      },
      fixedHeight]">
      <div v-if="showHeader"
        class="el-table__fixed-header-wrapper"

handleFixedMousewheel的具体内容,粗看下来确实与发现的问题相符

      handleFixedMousewheel(event, data) {
        const bodyWrapper = this.bodyWrapper;
        if (Math.abs(data.spinY) > 0) {
          const currentScrollTop = bodyWrapper.scrollTop;
          if (data.pixelY < 0 && currentScrollTop !== 0) {
            event.preventDefault();
          }
          if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
            event.preventDefault();
          }
          bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5);
        } else {
          bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5);
        }
      },

解决

简单粗暴,在mounted生命周期中覆盖方法。
这里ref的用法不清楚的直接看vue官方文档吧。

this.$refs.table.handleFixedMousewheel = function() {};

2021-12-3 更新

看了评论区有很多人反馈无效。。我这里自己测试是有效的,不过有一点需要关注

这里个滚动事件监听对象需要根据各自情况改变,本意就是在滚动时调用scrollBarFixedHandle方法,重新计算各数值。

document.addEventListener("scroll", this.scrollBarFixedHandle);

这里附一个codepen
https://codepen.io/lazysleep/pen/XWebOxP

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值