前言Github原文:github.com/qiud...

本文介绍内容包括:

1. Element UI 实现表头表列固定思考与总结

2. translate3d如何实现表头表列固定

书承上文,在前文【Vue进阶】青铜选手,如何自研一套UI库中介绍了Vue组件库的开发细节,举例实现了button、table等组件的开发。在Ange这个UI库中,我实现了一个内容高可定制的表格组件:可固定表头和表列,内容则自行定义。

首先要承认,这个table组件实现的功能很简单:创建表格展示数据

可固定表头

可固定表列

可实现简易版多级表头

表格组件是UI库里面最为复杂的组件之一,项目中使用表格的场景特别多,我们很难覆盖所有人的需求,比较常见的就有:固定表头

固定表左/右侧列

多级表头

勾选行数据

展开行数据

数据排序

从作用对象来看,这些需求又可归为影响布局(Eg: 固定表头表列)和影响数据(Eg: 勾选数据)两个大类。在Ange UI的table组件中,仅仅实现了影响布局这个类下面的部分功能,该组件不操作数据,甚至具体到使用tr、td标签(以及td里面如何包裹数据)展示数据也是由使用者自己定义的。狠狠点击这里在线查看示例,或者查看代码:

姓名{{ each }}{{ each.name }}{{ each.verdict }}{{ each.song }}

通过插槽slot指定thead或是tbody。简单就意味着精细和可拓展性强,同时带来的问题就是用户的使用成本高了(比如实现数据选择功能,当然ag-table在不操作源数据的原则下也能拓展出这个功能)。

谈谈element的固定表头表列

从浏览器中审查Element table组件的渲染效果看,Element实现固定表头表列的方式是:将固定的部分(如表头)和不固定的部分(如表体)拆分放在不同区域(不同的div下),设置表体所在区域可滚动即可,然后再通过一定的手段(如阴槽、表格数据备份)去同步不同区域之间的布局。

在一篇饿了么专题的文章中,详细阐述了固定表头表列的实现。下面简单总结并整理其中存在的问题。

1.1 固定表头的思路

从浏览器中审查table组件的渲染效果看:

表头和表体分别放在了两个不同的div区域:el-table__header-wrapper & el-table__body-wrapper,如此表体内容超出容器高度时,会出现滚动条,只在自己区域内滚动,达到了表头固定的效果。这样的实现导致了两个问题:两个表格宽度不一致:表体所在的区域多出了一条滚动条

两个表格之间的列宽如何保持一致

针对上面的问题,element也做了处理,引用饿了么文中一张图片:

在表头部分增加一个Gutter元素,虚拟成滚动条去占据一定宽度(图片右上角粉色的竖条),宽度一致的处理则是要求用户使用的时候个传入每个列的宽度。

这种实现方式有什么缺点呢?额外维护新增元素(Gutter);

自定义每列宽度增加用户使用成本,理想情况应该能根据文本内容自适应;

表体的滚动条上不去(滚不到表头的顶部),这个让我很捉急;

表头仅是相对于表体的固定,能实现相对于窗口的fixed吗?

1.2 固定表列的思路

实现固定表列相对比较复杂,为实现这个功能,element可谓是付出了“巨大的成本”。在这个左右列固定的渲染效果中:

渲染出了3份表格:el-table__header-wrapper & el-table__body-wrapper 是表体区域,el-table__fixed 是左固定列区域、el-table__fixed-right 是右固定列区域),每一份表格又有2个table,一共是6个table;通过设置左右区域绝对定位和宽度实现固定的效果。

这样实现会有什么问题呢?一份表格数据被渲染成三份,放大了三倍的DOM开销。(这也是element -table在数据量大或者未分页的情况下,页面卡顿,性能降低的根本原因)

同步鼠标的scroll事件:在一个区域内滚动需要在其他两个区域作同步滚动

额外维护固定列样式和内容(如宽度等)

基于此,Ange UI的table实现考虑用另外一种方式去实现,达到了最低的DOM成本。

getBoundingClientRect

在介绍固定表头表列实现方法之前,先科普下getBoundingClientRect这个API。getBoundingClientRect()方法返回元素的大小及其相对视口的位置,它的返回值是一个DOMRect对象。DOMRect对象包含了一组用于描述边框的只读属性:left、right、top、bottom,单位为像素。除了width和height外的属性都是相对于视口的左上角而言的。

如下图:

实现固定表头

在一个table中分别用thead和tbody展示表头表体,如下代码:

:style="theadStyle">

监听页面滚动事件,计算table的位移,使用translate3d反向设置thead的y轴位移值,达到固定表头的效果。如下图:

滚动页面滚动条,table由top1(正值)位置移动到top2(负值)位置,那么,要使thead在触碰到页面顶端时(即top=0),继续移动,thead就要设置成 translate3d(0px, -top2, 0px)。这样,thead就一直处在页面顶端位置了。 在某些场景下,thead达到Header的位置时就应该被fixed了,那们我们可以设置一个offsetTop参数,用户自定义偏移值,thead在top=0 - offsetTop时被fixed。看关键实现代码:

export default {

data () {

return: {

fixed: { // fixed状态 top: false

},

clientRect: { // 位移值 top: 0

}

}

},

computed: {

theadStyle () {

const { top } = this.clientRect

return {

transform: `translate3d(0px,${this.fixed.top

? -top

: 0}px, 1px)`

}

}

},

watch: {

'clientRect.top': function (val) {

// 判断到DOMRect的top值小于0时,开始fixed this.fixed.top = val < 0

}

},

mounted () {

// 监听页面滚动事件,获取table对象的DOMRect属性 window.addEventListener('scroll', this.scrollHandle, {

capture: false,

passive: true

})

},

methods: {

scrollHandle () {

const $table = this.$refs.table

if(!$table) return

const { top } = $table.getBoundingClientRect()

this.clientRect.top = Math.floor(top - parseInt(this.offsetTop, 10))

}

}

}

结合 @前言 部分ag-table的使用示例,在中传入一个offsetTop参数,即可实现thead在指定位置的fixed。另由于thead和tbody在同一个table中,不需要维护每一列的宽度,它可以根据内容自适应。查看demo。

实现固定表列

固定列的实现需要三个表格(分别固定左列和右列),如下代码:

ref="leftTable"

:style="leftStyle">

:style="theadStyle">

:style="theadStyle">

ref="rightTable"

:style="rightStyle">

:style="theadStyle">

table横向滚动时,计算容器的横向滚动距离scrollLeft,使用translate3d反向设置左table的x轴位移值,固定左列;对于右table,先要将其初始位置设置在容器的最右端,横向滚动时再结合scrollLeft设置x轴的位移值。如下图:

初始化时,rightTable的横向位移值:$rightTable.right - $container.right,leftTable就是0;发生横向滚动时,leftTable的横向位移值:scrollLeft,rightTable的位移值:初始位移 - scrollLeft。看关键实现代码:

export default {

computed: {

leftStyle () { // 左侧表格位移 const { left } = this.clientRect

return {

transform: `translate3d(${this.fixed.left

? left

: 0}px, 0px, 1px)`

}

},

rightStyle () { // 右侧表格位移 const { right } = this.clientRect

return {

transform: `translate3d(${-right}px, 0px, 1px)`

}

}

},

watch: {

'clientRect.left': function (val) {

// 横向滚动距离为正,开始设置fixed this.fixed.left = val > 0

}

},

mounted () {

// 存在由表格时设置其初始位移 if(this.hasRightTable) {

const container = this.$refs.container.getBoundingClientRect()

const rightTable = this.$refs.rightTable.getBoundingClientRect()

this.clientRect.right = Math.floor(rightTable.right - container.right)

// 记录右表格初始位移值 this.initRight = this.clientRect.right

}

// 监听表格容器的滚动事件 this.$refs.container.addEventListener('scroll', this.scrollXHandle, {

capture: false,

passive: true

})

// ... },

methods: {

scrollXHandle () {

// ... this.clientRect.left = Math.floor(this.$refs.container.scrollLeft)

const right = Math.floor(this.initRight - this.$refs.container.scrollLeft)

this.clientRect.right = right

}

}

}

按照这个思路实现左右列固定,效果如下(在线查看):

同步Hover效果

最后一步,因为这个表格是由三份table组成,因此当鼠标hover在其中一个table行上时,需要在剩余两个table的对应行中同步hover效果。看关键代码的实现:

export default {

mounted () {

if(this.hasLeftTable || this.hasRightTable) {

// 定义鼠标hover事件 this.$el.addEventListener('mouseover', this.mouseOver, false)

this.$el.addEventListener('mouseout', this.mouseLeave, false)

}

},

methods: {

mouseOver (e) {

this.hoverClass(e, 'add')

},

mouseLeave(e) {

this.hoverClass(e, 'remove')

},

hoverClass(e, type) {

const tr = e.target.closest('tr')

if(!tr) {

return

}

const idx = tr.rowIndex // 当前hover行的编号 const trs = querySelectorAll(`tbody tr:nth-child(${idx})`, this.$el)

if(trs.length === 0) {

return

}

// 对所有tbody下同一编号的tr添加hover类 trs.forEach(each => {

each.classList[type]('hover')

})

}

}

}

通过translate3d设置左右列的位移实现固定列的效果,避免了:多余的DOM开销:不需要新增额外DOM元素(Gutter),更需要复制多份DOM数据,将DOM开销减少到最小;

不需要维护不同表格之间列宽行高问题,完全自适应;

不需要在多个表格之间同步scroll事件

结语

table组件一直是开发复杂度较高的组件,既要考虑性能,又要考虑尽可能地对开发者使用友好。在此抛砖引玉,提供另一种开发思路,只为给有计划开发table组件的你提供一点帮助。

当然你有其他的想法欢迎评论一起交流~

The end.

el table 固定表头和首行_再谈table组件:固定表头和表列相关推荐

  1. el table 固定表头和首行_表头太太太复杂了,如何批量打印?简单!

    点击上方蓝字关注星标★不迷路 本文作者:小敏本文编辑:小叮.竺兰 打印工资条估计是财务老师的痛,要把一行行的数据,变成一条条的工资条. 数据很多,表头很复杂. 一个个复制粘贴?那是不可能的! 那怎么办 ...

  2. 固定表头和首行_Excel一步制作斜线表头!还有这些高分Excel表头技巧,看完秒会...

    大家做Excel表格的时候,应该都会遇到一些要用到特殊表头的情况吧,比如双栏斜线表头.三栏斜线表头.还有合并多个单元格居中文字的表头,这些操作其实都很简单,10秒都不用就能搞定. 1.两栏表头 先来说 ...

  3. WPF DataGrid 通过自定义表头模拟首行固定

    WPF DataGrid 通过自定义表头模拟首行固定 独立观察员 2021 年 9 月 25 日 最近工作中要在 WPF 中做个表格,自然首选就是 DataGrid 控件了.问题是,UI 设计的表格是 ...

  4. VBA小程序--针对所有已经打开的Excel文件_格式调整_针对所有工作表_冻结首行_无视所在位置

    VBA小程序--针对所有已经打开的Excel文件_格式调整_针对所有工作表_冻结首行_无视所在位置 Function 格式调整_针对所有工作表_冻结首行_无视所在位置()Dim sht As Work ...

  5. el table 固定表头和首行_vue表格实现固定表头首列

    前言 最近在做vue移动端项目,需要做一个可以固定表头首列的表格,而且由于一些原因不能使用任何UI插件,网上找了很久也没什么好方法,所以在解决了问题之后,写下了这篇文章供后来人参考,文章有什么错漏的问 ...

  6. axure中怎么做出固定首行_办公软件操作技巧078:如何在excel表格中冻结行与列...

    在日常工作中,有时我们编辑的excel表格会比较大,数据内容有很多行和列,当我们拖动滚动条找到了离表头比较远的数据行或列的内容时,又看不到行或列表头标题信息了,这时再去处理数据信息就会感觉很不方便,如 ...

  7. 再谈table组件:固定表头和表列

    前言 本文介绍内容包括: Element UI 实现表头表列固定思考与总结 translate3d如何实现表头表列固定 书承上文,在前文[Vue进阶]青铜选手,如何自研一套UI库中介绍了Vue组件库的 ...

  8. 【例子】固定表格的首行与首列

    思路如下图所展示,整个表格分为四个部分:L1区域固定不动,R2为主要滑动区域,L2和R1随着R2的滑动相应滑动 解决实例: 直接复制下方的全部代码即可,没有引用本地文件 下面是该实例的完整代码 < ...

  9. 原生table如何实现表格首行标题冻结

    移动端不用插件或者UI库的情况下,对原生写的table,想要实现首行标题行在滚动的时候保持冻结状态(即数据较多时能够吸顶),首先大家想到的是position:fixed属性,但是在表格布局下,你需要调 ...

  10. 常见花材的固定的方法有哪些_插花方法之巧妙固定花材

    传统的插花除了用一些基本器具来固定花材,还有很多其它的花材固定技巧,比如"做撒固定"."加易枝固定"."折枝固定"-- 插瓶花时,如果花枝短 ...

最新文章

  1. 在数据库创建表的时候,时间设置为什么类型,会随着每次提交的时间发生变化
  2. crtmpserver 配置说明_crtmpserver 流媒体服务器 集群 安装配置
  3. 个人贷款的条件、流程和注意事项
  4. Database之SQLSever:SQLSever数据表管理(GUI法/SQL语句命令法两种方法实现建立表、修改表,以及增、删、改、查)之详细攻略
  5. 大型电商项目3.0实战+支付宝、微信支付项目实战
  6. 基因分子生物学(2)-DNA携带遗传特性
  7. 平衡二叉树,AVL树之图解篇
  8. iTOP-4418开发板支持动态调频,AXP228电源管理,预留锂电池接口,内置充放电电路及电量计...
  9. asp导出excel文件格式
  10. html怎么使表格居中,html怎么使表格居中
  11. 【分布式开发】之 CAP 原则
  12. idea git切换分支、拉取最新代码 自己的代码被覆盖怎么办
  13. 遇到“此网站的安全证书有问题”怎么办
  14. 在Excel中如何制作K线
  15. 基于Redis解决业务场景中延迟队列的应用实践
  16. 人工智能之基于多变量线性回归的房屋销售价格预测详细解决方案
  17. X的N次方求解——pow(x,n)实现
  18. 引领行业革新:创维游戏装备S81 Pro重磅发布
  19. 笔记本接投影仪共同显示的方法
  20. CTP显示: 报单被拒绝,SHFE:不被支持的报单类型

热门文章

  1. 【图解算法】最小生成树
  2. fiddler 抓包下载钉钉直播回放
  3. VAX 过期重新安装办法。
  4. 路由器linux校园网,还在为无法突破校园网的限制而烦恼?动动手,借助路由器完成锐捷认证...
  5. WinMTR-路由追踪软件
  6. 想要艺术气质高逼格,不吹牛真的有捷径可走
  7. CFA一级考试题型是什么?好不好考?
  8. cfa英语不好的怎么学_英语不好能考CFA吗?看看他是怎么做到的
  9. ARM嵌入式开发,高通MSM8937核心板h
  10. 高通平台,MSM8937/MSM8953 RF配置流程