vue完美实现el-table列宽自适应


Posted in Vue.js onMay 08, 2021

背景

Element UI 是 PC 端比较流行的 Vue.js UI 框架,它的组件库基本能满足大部分常见的业务需求。但有时候会有一些定制性比较高的需求,组件本身可能没办法满足。最近在项目里就碰到了。

很多页面都需要用到表格组件el-table。如果没有给el-table-column指定宽度,默认情况下会平均分配给剩余的列。在列数比较多的情况,如果el-table宽度限定在容器内,单元格里的内容就会换行。强制不换行,内容要么在单元格内滚动,要么就会溢出或被截断。

产品想要的效果是:内容保持单行显示,列间距保持一致,表格超出容器允许水平滚动。el-table-column是支持设置固定宽度的,在内容宽度可预知的情况下,也能满足这个需求。问题就在于如何让列宽动态适应内容的宽度。在官方文档也没找到这样的选项,应该是组件本身不支持。

技术方案

于是想到了动态计算内容宽度的方案。网上也有人提过这个思路,做法是根据内容字符数来计算宽度。这种方法有几个局限:

  • 内容必须是文本
  • 不同字符宽度不一,结算结果不够准确
  • 需要在渲染前操作数据,不利于解耦

我采用了另一种思路,还是动态计算内容宽度,但是根据实际渲染后的 DOM 元素宽度,这样就能解决上面三个问题。

具体怎么做呢?通过查看渲染后的 DOM 元素发现,el-table 的表头和内容分别用了一个原生table,通过colgroup设置每列的宽度。就从这里入手,col的name属性值和对应的 td的class值是一致的,这样就可以遍历对应列的所有单元格,找出宽度最大的单元格,用它的内容宽度加上一个边距作为该列的宽度。

vue完美实现el-table列宽自适应

具体实现

怎么计算内容宽度呢?这是个比较关键的步骤。渲染后的每个单元格有个.cell类,用white-space: nowrap; overflow: auto;设置为不允许换行,内容超出后可滚动,同时设置display: inline-block;以便计算实际内容宽度。这样,最终的宽度可通过.cell元素的scrollWidth属性得到。

function adjustColumnWidth(table) {
  const colgroup = table.querySelector("colgroup");
  const colDefs = [...colgroup.querySelectorAll("col")];
  colDefs.forEach((col) => {
    const clsName = col.getAttribute("name");
    const cells = [
      ...table.querySelectorAll(`td.${clsName}`),
      ...table.querySelectorAll(`th.${clsName}`),
    ];
    // 忽略加了"leave-alone"类的列
    if (cells[0]?.classList?.contains?.("leave-alone")) {
      return;
    }
    const widthList = cells.map((el) => {
      return el.querySelector(".cell")?.scrollWidth || 0;
    });
    const max = Math.max(...widthList);
    const padding = 32;
    table.querySelectorAll(`col[name=${clsName}]`).forEach((el) => {
      el.setAttribute("width", max + padding);
    });
  });
}

中间的探索过程比较繁琐,但最终的代码实现却非常简洁。在什么时候触发列宽计算呢?自然是组件渲染完成后。为了方便重用,我采用了 Vue 自定义指令的方式。

Vue.directive("fit-columns", {
  update() {},
  bind() {},
  inserted(el) {
    setTimeout(() => {
      adjustColumnWidth(el);
    }, 300);
  },
  componentUpdated(el) {
    el.classList.add("r-table");
    setTimeout(() => {
      adjustColumnWidth(el);
    }, 300);
  },
  unbind() {},
});

更进一步,我封装了一个 Vue 插件叫v-fit-columns,已经发布到 npm 仓库,直接安装即可使用。
安装:

npm install v-fit-columns --save

引入:

import Vue from 'vue';
import Plugin from 'v-fit-columns';
Vue.use(Plugin);

使用:

<el-table v-fit-columns>
  <el-table-column label="No." type="index" class-name="leave-alone"></el-table-column>
  <el-table-column label="Name" prop="name"></el-table-column>
  <el-table-column label="Age" prop="age"></el-table-column>
</el-table>

源码仓库在这:https://github.com/kaysonli/v-fit-columns ,欢迎各位不吝赐教和 Star!

总结

这个方案多少有点 Hack 的意味,只顾实现需求,可能在其他方面还有点瑕疵,比如渲染完后会稍微闪一下(因为要重新调整宽度,会出现 reflow)。不过从最终实现的效果来看,还算令人满意

以上就是vue完美实现el-table列宽自适应的详细内容,更多关于vue实现el-table列宽自适应的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue监听滚动事件的方法
Dec 21 Vue.js
全面解析Vue中的$nextTick
Dec 24 Vue.js
vue3中轻松实现switch功能组件的全过程
Jan 07 Vue.js
vue项目配置 webpack-obfuscator 进行代码加密混淆的实现
Feb 26 Vue.js
手写Vue2.0 数据劫持的示例
Mar 04 Vue.js
vue项目实现分页效果
Mar 24 Vue.js
vue中data改变后让视图同步更新的方法
Mar 29 Vue.js
vue引入Excel表格插件的方法
Apr 28 Vue.js
vue组件的路由高亮问题解决方法
May 11 Vue.js
深入理解Vue的数据响应式
May 15 Vue.js
vue-cropper组件实现图片切割上传
May 27 Vue.js
VUE之图片Base64编码使用ElementUI组件上传
Apr 09 Vue.js
关于Vue Router的10条高级技巧总结
May 06 #Vue.js
Vue项目中如何封装axios(统一管理http请求)
May 02 #Vue.js
使用vue-element-admin框架从后端动态获取菜单功能的实现
vue使用v-model进行跨组件绑定的基本实现方法
关于vue中如何监听数组变化
vue实现简单数据双向绑定
Apr 28 #Vue.js
vue引入Excel表格插件的方法
Apr 28 #Vue.js
You might like
php 服务器调试 Zend Debugger 的安装教程
2009/09/25 PHP
PHP图片加水印实现方法
2016/05/06 PHP
PHP实现小偷程序实例
2016/10/31 PHP
Thinkphp5.0 框架实现控制器向视图view赋值及视图view取值操作示例
2019/10/12 PHP
JavaScript国旗变换效果代码
2008/08/13 Javascript
javascript 限制输入和粘贴(IE,firefox测试通过)
2008/11/14 Javascript
精通JavaScript 纠正 cleanWhitespace函数
2010/03/11 Javascript
table行随鼠标移动变色示例
2014/05/07 Javascript
Lua表达式和控制结构学习笔记
2014/12/15 Javascript
node.js中的fs.appendFile方法使用说明
2014/12/17 Javascript
javascript格式化指定日期对象的方法
2015/04/21 Javascript
js验证上传图片的方法
2015/05/12 Javascript
jQuery根据name属性进行查找的用法分析
2016/06/23 Javascript
用js动态添加html元素,以及属性的简单实例
2016/07/19 Javascript
JavaScript toUpperCase()方法使用详解
2016/08/26 Javascript
Angular中使用$watch监听object属性值的变化(详解)
2017/04/24 Javascript
微信小程序 检查接口状态实例详解
2017/06/23 Javascript
JavaScript递归算法生成树形菜单
2017/08/15 Javascript
微信小程序实现tab和swiper切换结合效果
2020/07/17 Javascript
p5.js入门教程之小球动画示例代码
2018/03/15 Javascript
JS实现图片转换成base64的各种应用场景实例分析
2018/06/22 Javascript
[02:06]DOTA2肉山黑名单魔法终结者 敌法师中文配音鉴赏
2013/06/17 DOTA
[01:10:24]DOTA2-DPC中国联赛 正赛 VG vs Aster BO3 第一场 2月28日
2021/03/11 DOTA
Python中 传递值 和 传递引用 的区别解析
2018/02/22 Python
django小技巧之html模板中调用对象属性或对象的方法
2018/11/30 Python
python实现感知器算法(批处理)
2019/01/18 Python
Python操作Sonqube API获取检测结果并打印过程解析
2019/11/27 Python
基于Keras中Conv1D和Conv2D的区别说明
2020/06/19 Python
通用的Django注册功能模块实现方法
2021/02/05 Python
浅谈Html5多线程开发之WebWorkers
2018/05/02 HTML / CSS
2014年公司迎新年活动方案
2014/02/24 职场文书
党课心得体会范文
2014/09/09 职场文书
大学生见习报告范文
2014/11/03 职场文书
聘任证明怎么写
2015/03/02 职场文书
《落花生》教学反思
2016/02/16 职场文书
Vue过滤器(filter)实现及应用场景详解
2021/06/15 Vue.js