elementUI的el-table组件有固定列时垂直滚动错行且卡顿问题解决方案

4,276 阅读2分钟

最近在用elementUI的el-table组件开发列表,产品要求第一行、最后一行、第一列、最后一列固定,按照要求开发完成后,测试了下,发现两个问题。

问题1:固定列下方的滚动条不能滑动

此问题比较好解决,增加css样式处理即可。

.el-table .el-table__fixed {
  height: auto !important;
  bottom: 8px !important; //具体值是多少根据页面横向滚动条的高度进行设置
}

但是将show-summary设置为true时,表格尾部合计行的滚动条是显示在合计行的上方的,按上方样式设置,此时固定列的滚动条会被遮挡住,非固定列的滚动条可正常使用。

我这边最后一行固定采用的是css样式实现,而非组件自带的合计行,滚动条在最后一行的下方显示,所以采用上方样式设置,固定列的滚动条不会被遮挡,且可以正常滑动。

列表最后一行固定css方式实现代码如下:

  // 最后一行固定实现
  .tr-sum {
      background-color: #F7F9FC;
      bottom: 0;
      table-layout: fixed;
      position: sticky;
    }

其中,tr-sum是通过el-table组件的row-class-name属性给最后一行添加的类名。

问题2:有固定列时列表垂直滚动错行且卡顿

表格fixed左边或者fixed右边的时候,垂直滚动表格会出现滞缓导致视觉上错行且滚动不流畅问题。

导致问题的原因

  • el-table组件内部对滚动事件做了防抖,它不会实时修改非固定列的位置 ,有几毫秒的延迟。
  • 滚轮事件会自动触发滚动事件,滚动事件中自动使用了behavior: 'smooth'机制,这个机制会让滚动看起来很丝滑,但会耗费更多的性能,估计浏览器处于性能考虑导致两个滚动效果不同步了。

问题解决思路

  • 继承覆盖element的Table组件,并重写Table组件的bindEvents方法。
  • 取消掉滚轮事件的默认行为,我们自己手写滚动

问题解决步骤

1.在项目中新建tableMismatch.js文件,写上如下代码:

// 1.让Vue.component...这段注册代码早于Vue.use(ElementUI) 2.执行 delete Table._Ctor

import Vue from 'vue';
import { Table, TableColumn } from 'element-ui';
Vue.use(Table).use(TableColumn);

delete Table._Ctor;
const { bindEvents } = Table.methods;
Object.assign(Table.methods, {
  bindEvents() {
    bindEvents.call(this);
    this.bodyWrapper.addEventListener(
      'mousewheel',
      this.handleBodyMousewheel
    );     
  },
  handleBodyMousewheel(event) {
    const { fixedWrapper, rightFixedWrapper } = this.$refs;
    if (fixedWrapper) {
      const fixedBodyWrapper = fixedWrapper.querySelector(
        '.el-table__fixed-body-wrapper'
      );
      if (fixedBodyWrapper) {
        event.preventDefault();
          fixedBodyWrapper.scrollBy({
            left: event.deltaX,
            top: event.deltaY,
          });
          this.$refs.bodyWrapper.scrollBy({
            left: event.deltaX,
            top: event.deltaY,
            });
        }
      }
      if (rightFixedWrapper) {
        const fixedBodyWrapper = rightFixedWrapper.querySelector(
          '.el-table__fixed-body-wrapper'
        );
        if (fixedBodyWrapper) {
          event.preventDefault();
          fixedBodyWrapper.scrollBy({
            right: event.deltaX,
            top: event.deltaY,
          });
          this.$refs.bodyWrapper.scrollBy({
            right: event.deltaX,
            top: event.deltaY,
          });
        }
      }
    },
});
Vue.component(Table.name, Table);

2.在项目main.js文件中引入import '@/utils/tableMismatch.js'

以上操作,只能解决鼠标垂直滚动非固定列时的错行卡顿问题,当鼠标垂直滚动固定列时,还是会出现固定列与非固定列错行问题。

因为上述方法只在bodyWrapper上加了mousewheel事件,也就是说垂直滚动非固定列才会触发mousewheel事件,而垂直滚动固定列不会触发mousewheel事件。

尝试在上述bindEvents函数中获取固定列DOM从而给固定列加上mousewheel事件,但是获取不到。

所以只能另辟蹊径,进行如下操作了。

  1. 在表格绘制页面加上如下代码:
data() {
    return {
      tableBodyWrapper: null,
    };
},
mounted() {
  this.$nextTick(()=> {
    this.tableBodyWrapper = this.$refs.elTable.bodyWrapper;
    this.tableBodyWrapper.addEventListener('scroll',this.setScrollTop);
  });
},
beforeDestroy() {
    this.tableBodyWrapper.removeEventListener('scroll',this.setScrollTop);
},
methods: {
  setScrollTop() {
    const scrollTop = this.tableBodyWrapper.scrollTop;
     this.$refs.table.$refs.fixedBodyWrapper.scrollTop = scrollTop;
  }, 
}

el-table设置了固定列后就变成了两个表格,将非固定列的滚动高度赋值给固定列,这样保持两个表格的高度统一。

其实只要步骤3,就可以解决固定列与非固定列滚动错行问题,但是步骤3不能解决滚动卡顿问题。想要解决垂直滚动卡顿问题,还是要加上步骤1、步骤2。