实现el-table自适应高度(自定义指令踩坑版)

问题

使用 ElementUI 的 el-table 组件,默认情况下,当每页的数据过多时,浏览器就会撑开产生纵向的滚动条,这导致用户上下滚动查找表格数据时很不方便(滚到顶部看不到分页栏、滚到底部看不到搜索栏)

因此想把表格改成整个页面不产生纵向滚动条,即可视区域内能看到搜索栏+表格数据+分页栏,数据过多时,让表格自适应高度产生滚动条

思路

想实现上述需求的关键就是让表格自适应高度

首先我自己使用过的方法是通过 window.onresize 动态计算高度,然后赋值给 table 的 height 属性,但是这种方法先且不说能完全实现(如果搜索栏是可以展开收起的,这种情况没法监听window.onresize),光是使用就比较繁琐,先要封装计算方法,然后组件里还得写三步,不方便开发者使用。

js 复制代码
export default function() {
  return {
    /**
     * 基于Vue全局的封装 监听table高度变化
     * @param {String} myRef 表格的ref属性,不传默认是'table'。补充:就算默认table,也要确保有ref='table'属性,不然报错
     * @param {Number} customHeight 额外可调整的高度,如页码部分的高度
     */
    tableHeight: (myRef = 'table', customHeight = 0) => {
      this.$nextTick(() => {
        // window.innerHeight 可视区域的高度
        // this.$refs[myRef].$el.getBoundingClientRect().top 该元素到可视区域顶部的距离
        const offsetHeight = this.$refs[myRef].$el.getBoundingClientRect().top
        this.tableHeight = window.innerHeight - offsetHeight - customHeight
        window.onresize = () => {
          this.tableHeight = window.innerHeight - offsetHeight - customHeight
        }
      })
    }
  }
}
js 复制代码
import heightUtils from '@/utils/tableHeight'
Vue.prototype.$heightUtils = heightUtils
js 复制代码
data() {
  return {
    tableHeight: 0
  }
},
mounted() {
  this.$nextTick(() => {
    this.$heightUtils().tableHeight('table', 120)
  })
}

<template> 
  <table :height="tableHeight" />
</template>

以上方法固然不适用了,那么就换一种实现方式,其实网上有很多相关文章,但是我看百分之八十都是cv的,或者也有可能那些作者只考虑了最简单的表格场景,下面介绍我自己实践后的版本。

自定义指令

自定义指令

封装

directive/table/tableHeight.js

js 复制代码
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'

// 设置表格高度
const doResize = (el, binding, vnode) => {
  // 获取表格Dom对象
  const { componentInstance: $table } = vnode
  // 获取调用传递过来的数据
  const { value } = binding
  // 没有直接终止
  if (!$table) return
  // 获取距底部距离(用于展示页码等信息)
  const customHeight = (value && value.customHeight) || 30
  // 计算列表高度并设置
  const height = window.innerHeight - el.getBoundingClientRect().top - customHeight
  $table.layout.setHeight(height)
  $table.doLayout()
}

export default {
  // 初始化设置
  bind(el, binding, vnode) {
    // 设置resize监听方法
    el.resizeListener = () => {
      doResize(el, binding, vnode)
    }
    // 绑定监听方法到addResizeListener
    addResizeListener(window.document.body, el.resizeListener)
  },
  // 所在组件的 VNode 更新时设置
  // 如果搜索栏是可以展开收起的,用此方法更新
  update(el, binding, vnode) {
    doResize(el, binding, vnode)
  },
  // 销毁时设置
  unbind(el) {
    // 移除resize监听
    removeResizeListener(window.document.body, el.resizeListener)
  }
}

directive/index.js

js 复制代码
import tableHeight from './table/tableHeight'

const install = function(Vue) {
  Vue.directive('tableHeight', tableHeight)
}

export default install

main.js

js 复制代码
import directive from './directive'
Vue.use(directive)

使用

js 复制代码
<template>
  <table v-tableHeight="{customHeight: 76}" />
</template>

踩坑

以上代码的实现思路是网上能搜索到的(vue2/vue3都有,因为大体思路是一致的),但是其实是有坑的!

  1. 没有添加 overflow: auto 导致表格不能滚动,可以在table中添加style样式

  2. 表头没有固定,需要额外写样式 ::v-deep .el-table__header-wrapper{position: sticky; top: 0; z-index: 1;}

  3. 对于设置了 fixed 属性的table列,会出现问题,暂无解决方法

出现以上问题我的猜想是: $table.layout.setHeight(height) 只是单单设置了表格的高度,但其实 table 组件中的的 height 属性传进去之后,源码做了其他处理,比如fixed的高度等。

那么是不是可以把这个自适应高度添加到一个div外壳上,让div的高度自适应,然后table添加 height: 100%

完善

directive/table/tableHeight.js

js 复制代码
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'

// 设置表格高度
const doResize = (el, binding) => {
  // 获取调用传递过来的数据
  const { value } = binding
  // 获取距底部距离(用于展示页码等信息)
  const customHeight = (value && value.customHeight) || 76
  // 计算列表高度
  const height = window.innerHeight - el.getBoundingClientRect().top - customHeight
  // 设置高度
  el.style.height = height + "px"
}

export default {
  // 初始化设置
  bind(el, binding) {
    // 设置resize监听方法
    el.resizeListener = () => {
      doResize(el, binding)
    }
    // 绑定监听方法到addResizeListener
    addResizeListener(window.document.body, el.resizeListener)
  },
  // 所在组件的 VNode 更新时设置
  // 页面上搜索表单是可以展开收起的,当展开更多表单搜索时,表格高度没变
  update(el, binding) {
    doResize(el, binding)
  },
  // 销毁时设置
  unbind(el) {
    // 移除resize监听
    removeResizeListener(window.document.body, el.resizeListener)
  }
}

directive/index.js

js 复制代码
import tableHeight from './table/tableHeight'

const install = function(Vue) {
  Vue.directive('tableHeight', tableHeight)
}

export default install

main.js

js 复制代码
import directive from './directive'
Vue.use(directive)

组件中

js 复制代码
<template>
  <div v-tableHeight="{customHeight: 76}">
    <table height="100%" />
  </div>
</template>

总结

大功告成,在自己项目中测了很多种情况,暂时未发现什么问题,如果jym发现我这种方式还存在问题或者有其他更便捷的方法,欢迎评论区交流!

相关推荐
纸上的彩虹1 小时前
半年一百个页面,重构系统也重构了我对前端工作的理解
前端·程序员·架构
be or not to be1 小时前
深入理解 CSS 浮动布局(float)
前端·css
LYFlied2 小时前
【每日算法】LeetCode 1143. 最长公共子序列
前端·算法·leetcode·职场和发展·动态规划
老华带你飞2 小时前
农产品销售管理|基于java + vue农产品销售管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小徐_23332 小时前
2025 前端开源三年,npm 发包卡我半天
前端·npm·github
C_心欲无痕2 小时前
vue3 - 类与样式的绑定
javascript·vue.js·vue3
GIS之路3 小时前
GIS 数据转换:使用 GDAL 将 Shp 转换为 GeoJSON 数据
前端
JIngJaneIL3 小时前
基于springboot + vue房屋租赁管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
天天扭码3 小时前
以浏览器多进程的角度解构页面渲染的整个流程
前端·面试·浏览器
你们瞎搞3 小时前
Cesium加载20GB航测影像.tif
前端·cesium·gdal·地图切片