手撸一个Table组件(Table组件不过如此)
一、前言
手写Table组件这个文章我一直都想写,今天终于得空来写它了。小编认为Table组件是组件库里"较为复杂"的一个组件,因为它的扩展性非常强,并且它的基础样式如何去写都非常考究,那么今天我就带大家来实现一个基础功能的table组件,废话不多bb,进入正题吧。
二、实现哪些功能?
- 最基础的table布局
- 固定头
- 固定列
三、实现的效果
以及组件的使用方式期望是这样:
const column = [{ name: '序号', key: 'order', width: '100px', isFixed: true },{ name: '第二列', key: 'col2', width: '200px' },{ name: '第三列', key: 'col3', width: '300px' },{ name: '第四列', key: 'col4', width: '200px' },{ name: '第五列', key: 'col5', width: '500px' },{ name: '第六列', key: 'col6', width: '200px' },
];
const data = [{ order: 1, col2: '1-2', col3: '1-3', col4: '1-4', col5: '1-5', col6: '1-6' },{ order: 2, col2: '2-2', col3: '2-3', col4: '2-4', col5: '2-5', col6: '2-6' },{ order: 3, col2: '3-2', col3: '3-3', col4: '3-4', col5: '3-5', col6: '3-6' },{ order: 4, col2: '4-2', col3: '4-3', col4: '4-4', col5: '4-5', col6: '4-6' },{ order: 5, col2: '5-2', col3: '5-3', col4: '5-4', col5: '5-5', col6: '5-6' }
];
<CustomTablecolumn = { column } data = { data } rowHeight = { '100px' } />
四、动手实现
4.1、table组件的尺寸
4.1.1、如何定义table组件的宽?
对于table组件来说,定义宽度有2种方式,一种是"固定宽度布局",另一种是"自由宽度布局"。我们可以通过"table-layout"属性在这2种方式中做出选择。
4.1.1.1、“固定宽度布局”
就是显示的定义table标签的宽度(即table-layout: fixed),那么此时
<table>
的宽度 与 各列[td | th]
的宽度的关系如下:
- table真实的width = Math.max(table.style.width, 各列宽度之和)
- 如果 table.style.width > 各列宽度之和,那么
二者之差再除以列数
得到的结果将增加到各列上。
纸上得来终觉浅,我们来跑下代码加深一下上述节论的记忆(以下均是react环境下的代码
):
table {table-layout: fixed;border-collapse: collapse;border-spacing: 0px;width: 200px;
}
<div className = 'yon-table-box'><table><colgroup><col style={{ width: '100px' }}><col style={{ width: '100px' }}><col style={{ width: '100px' }}><colgroup><thead><th>列1</th><th>列2</th><th>列3</th></thead></table>
</div>
此时效果如下:
此时我们再稍微改动下样式,css如下:
table {table-layout: fixed;width: 500px;border-collapse: collspse;border-spacing: 0px;
}
效果如下:
此时我们发现,上述结论是对的。
4.1.1.2、“自由宽度布局”
顾名思义,更容易实现这个等式 “table.style.width == 各列宽度之和 + 边框 + 间距”。通过设置 table-layout: auto 来触发。
我们来跑下代码试一下:
table {table-layout: auto;border-collapse: collapse;border-spacing: 0px;width: auto;
}
<div className = 'table-box'><table><colgroup><col style={{ width: '100px' }}><col style={{ width: '100px' }}><col style={{ width: '100px' }}></colgroup><thead><th>列1</th><th>列2</th><th>列3</th></thead></table>
</div>
此时我们来看下效果:
4.1.1.3、table宽度的最终选择
经过上面2种布局的分析,我们决定选用"自由宽度布局"的方式定义table的宽度,此时咱们的CustomTable组件的代码如下:
.yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;}th {background: #f4f4f4;}
import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)}render (){const { column } = this.props;return <div className = 'yon-table-box'><div className = 'right'><table><colgroup>{column.map(colItem => {return <col style={{ colItem.width }}>})}</colgroup><thead><tr>{column.map(colItem => {return <th>{ colItem.name }</th>})}</tr></thead></table></div></div>}}
Table的使用:
import React from 'react';const column = [{ name: '列1', key: 'col1', width: '100px' },{ name: '列2', key: 'col2', width: '200px' },{ name: '列3', key: 'col3', width: '300px' },];<CustomTable column = { column } />
4.1.2、如何定义table组件的高?
定义table组件的行高度,这个就比较简单了,我们只需要保证 table.style.height = ‘auto’, 即可实现自定义行高,CustomTable组件的代码如下:
.yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;// 新增代码+++++++++++++++++++++height: auto;}th {background: #f4f4f4;}// 新增代码++++++++++++++++++++++++++td {background: #fff;border-bottom: 1px solid rgb(208, 208, 208);}
import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)}render (){const { column, data, rowHeight } = this.props;return <div className = 'yon-table-box'><div className = 'right'><table><colgroup>{column.map(colItem => {return <col style={{ colItem.width }}>})}</colgroup><thead><tr>{column.map(colItem => {return <th>{ colItem.name }</th>})}</tr></thead><tbody>{// 新增代码+++++++++++++++++++++++++data.map(dataItem => {return <tr>{column.map(colItem => {return <td style={{ height: rowHeight }}>{dataItem[colItem.key]}</td>})}</tr>})}</tbody></table></div></div>}}
好啦,此时咱们的第一个功能到这就算实现啦,如果你是一步一步跟下来的,那么此时的效果应该是这样的:
4.2、固定列
我们先来看一下下面的图:
首先,根据目前实现的效果来看,我们可以得出以下信息:
- 目前dom上就只有一个table,意味着 表头、表体是连着的,符合正常文档流所有的特点。
- 红色框是我们想要固定的列。
好,此时我们来想一下能够做到固定列(无非是2个div互不干扰呗)的方法有哪些?
4.2.1、方式一
把一个table分为左右2个table,并保证只有右侧的table局部滚动
,干掉父div的scroll。
4.2.2、方式二
依旧是一个table分为左右2个table,只是在不改变现在滚动的情况下
,给左侧的table新增一个"sticky"的粘性定位即可。
4.2.3、方式三
利用js,监听scroll事件,在滚动的时候,利用transform属性,动态的设置translateX,来达到固定列的效果
4.2.3、固定列的最终选择
这里我们选择 方式二。理由非常简单,改动量非常少的情况就可以实现,其他的2种情况的改动都挺大,但是也可以实现,下面我会把其他的2种方式的伪代码写出来供大家参考。
- 方式一。其实这种方式更适合"固定宽度布局"的table,因为这种table的width是非常容易拿到的,你只需要保证列数过多的时候出现滚动条就可以(也仅仅是一行样式的问题),伪代码如下:
import React from 'react';export default class App extends React.Component {constructor(props){super(props)}render (){return <div style={{ display: 'flex' }}><!-- 左侧固定列 --><div className = 'left'><table></table></div><!-- 右侧自由列 --><div className = 'right' style={{ overflow: 'scroll' }}><table></table></div></div>}}
- 方式三。这种利用js的方式是最看重代码结构的,而且还会使你的代码变得非常臃肿(不利于拆分),它比较适用于下面的写法:
import React from 'react';// 大家看到这样的写法其实可能感觉不到什么,但是如果这样写的话,table组件的抽取将会异常困难。class App extends React.Component {constructor(props){super(props)this.leftTableRef = React.createRef();this.scrollTableBox = React.createRef();}scrollTable = () => {// 保持左侧固定不动this.leftTableRef.current.style.transform = `translateX(-${this.scrollTableBox.current.scrollLeft}`;}render (){return <div ref = { this.scrollTableBox } style={{ display: 'flex', overflow: 'scroll' }} onScroll = { () => this.scrollTable() }><!-- 左侧固定列table --><table ref={ this.leftTableRef }></table><!-- 右侧自由列table --><table></table></div>}}
4.2.4、固定列的最终代码
.yon-table-box {width: 100px;height: 100px;overflow: scroll;}table {table-layout: auto;table-collapse: collapse;table-spacing: 0px;width: auto;// 新增代码+++++++++++++++++++++height: auto;} th { background: #f4f4f4;} // 新增代码++++++++++++++++++++++++++td {background: #fff;border-bottom: 1px solid rgb(208, 208, 208);}// 新增代码++++++++++++++++++++++++++.left {position: sticky;left: 0px;}
import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)this.state = {// 固定列集合fixedColumnArr : [];// 自由列集合notFixedColumnArr: [];}}componentDidMount(){const { column } = this.props;this.setState(state => {return {// column配置项中,isFixed用于标识是否是固定列fixedColumnArr : column.filter(item => item.isFixed),notFixedColumnArr: column.filter(item => !item.isFixed)}})}render (){const { data, column, rowHeight } = this.props;const { fixedColumnArr, notFixedColumnArr } = this.state;return <div className = 'yon-table-box'>{ fixedColumnArr.length > 0&& <div className = 'left'> <table> <colgroup> { fixedColumnArr.map(item => { return <col style={{ minWidth: item.width, width: item.width }}> }) } </colgroup> <thead> <tr> { fixedColumnArr.map(item => { return <th>{item.name}</th> }) } </tr> </thead> <tbody> { data.map(dataItem => { return <tr> { fixedColumnArr.map(colItem => { return <td>{dataItem[colItem.key]}</td> }) } </tr> }) } </tbody> </table> </div>}<div className = 'right'><!-- 跟left代码一样,只需要把 fixedColumnArr 换成 notFixedColumnArr 即可 --></div></div>}}
4.3、固定头
固定头的实现思路 其实和 固定列的思路是一样的,这里就不具体分析了,夜也深了,哈哈哈,这里我就偷点懒,给伪代码了:
import React from 'react';class CustomTable extends React.Component {constructor(props){super(props)this.state = {// 固定列集合fixedColumnArr : [];// 自由列集合notFixedColumnArr: [];}}render (){const { data, column, rowHeight } = this.props;return <div className = 'yon-table-box'>// 整张表的头部(整个头部是需要sticky的)<div className = 'top'><div className = 'left'></div><div className = 'right'></div></div>// 整张表的body(左侧需要固定的body是需要sticky的)<div className = 'bottom'><div className = 'left'></div><div className = 'right'></div></div></div>}}
五、最后
好啦,table组件这次就分享到这里了,文章里有讲的不对的地方,欢迎大家指正,如果大家对table组件的实现有什么好的想法,也欢迎评论留言,那么886~~~
最后
为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。
有需要的小伙伴,可以点击下方卡片领取,无偿分享
相关文章:

手撸一个Table组件(Table组件不过如此)
一、前言 手写Table组件这个文章我一直都想写,今天终于得空来写它了。小编认为Table组件是组件库里"较为复杂"的一个组件,因为它的扩展性非常强,并且它的基础样式如何去写都非常考究,那么今天我就带大家来实现一个基础…...
Python|Leetcode刷题日寄Part01
Python|Leetcode刷题日寄Part0101:两数之和02:无重复字符的最长子串03:两数相加04:反转链表05:有效的括号06:回文数07:删除有序数组中的重复项08:删除链表的倒数第N个结点09…...
微信小程序更改头像昵称
背景 前面写了一篇关于小程序头像昵称获取更改的方案,有很多小伙伴私信我发一个整体的逻辑思路! 解决思路 前面的这篇文章中我们给出了页面中获取头像昵称的代码: <view class"headInfo" data-weui-theme"{{theme}}&qu…...

Linux 基础知识之文件系统
目录一、文件系统1.文件种类2.Linux和Windows文件后缀的不同3.查看文件类型3.绝对路径与相对路径二、系统分区三、目录结构一、文件系统 1.文件种类 Linux中一切皆文件。目光所及,皆是文件。文件的种类共有七种,每种文件都有自己的独特标识:…...

LeetCode 36. 有效的数独
LeetCode 36. 有效的数独 难度:middle\color{orange}{middle}middle 题目描述 请你判断一个 9x99 x 99x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。 数字 1−91-91−9 在每一行只能出现一次。数字 1−91-91−9 在每一列…...
2023-02-22 cascades-columbia-核心处理记录
摘要: columbia是哥伦比亚对于cascades的一个改进, 并且paper写的也相对详尽. 虽然cacades的实现有很多,比较出名的就是greenplum的gporca, 不过columbia也有其显著的优点. 本文通过对columbia的分析展开对cascades优化器思想的探讨. 参考: 2023-02-10 哥伦比亚cascades-xu-…...

华为分布式存储(FusionStorage)
Server SAN SAN:存储区域网络 IP SAN:以太网交换机和普通网线连接的存储,交换机之间做堆叠FC SAN:FC(光纤)交换机和光纤连接的存储,交换机之间做级联Server SAN:可以使用以太网交换机…...

说说 React 中 fiber、DOM、ReactElement、实例对象之间的引用关系
原生组件 fiber 原生组件 fiber,指的就是 type 为 “span”、“div” 的 fiber。 1.fiber.stateNode 指向真实 DOM 节点;2.node["__reactFiber$" randomKey] 指向对应 fiber,使用随机数是防止和业务代码的属性名冲突,…...
LaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式)
文章目录 LaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式)refnotedemoLaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式) ref markdown中公式编辑教程 在 Microsoft Word 中使用 LaTeX 输入数学公式【比较全,介绍了支持的语法和不支持的语法】 用wo…...

S5P6818_系统篇(2)源码编译及烧录
源码获取 源码获取和操作流程 1.下载liunux下的系统制作脚本,可以烧录系统和构建镜像 git clone https://github.com/friendlyarm/sd-fuse_s5p6818.git 如果出现git错误可使用如下方法: git config --global http.sslverify false 2.阅读该工具rea…...

LDPC码的编译码原理简述
关于fpga调用ldpc IP core的相关参数问题可以看我的另一篇文章 LDPC码由Gallager在1962年提出,全称为 Low Density Parity-check Codes 低密度奇偶校验码 它的译码性能可以逼近Shannon信道容量限,广富盛名的Turbo码也被证明是LDPC码的一个特例。并且LDPC…...

网络安全——数链路层据安全协议
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.数据链路层安全协议简介 1.数据链路安全性 二.局域网数据链路层协议 1.…...

spring的启动过程(一) :IOC容器的启动过程
一、web容器的加载 首先我们要先知道一个web项目的启动过程。 将Web项目部署到Tomcat中的方法之一,是部署没有封装到WAR文件中的Web项目。要使用这一方法部署未打包的webapp目录,只要把我们的项目(编译好的发布项目,非开发项目&am…...

这次,我的CentOS又ping不通www.baidu.com了(gateway配置)
当我们保证了宿主机与虚拟机的ip地址在同一网段,并且我们使用虚拟机ping宿主机,与宿主机ping虚拟机都可以互相ping通的情况下虚拟机却ping不通外网了,由于涉及到了跨越网络访问,所以我们应该把问题聚焦在网关的配置上!…...

启智社区“我为开源狂”第六期活动小白教程之基础活跃榜
一、写在前面 春天来啦~启智社区第六期活动也来啦! 有奖金的哦~~ 基础活跃榜奖金根据用户活跃程度进行100-300元的激励。 挑战升级榜需要用户完成相应任务,达标者可获得300-1000元的激励。 邀请助力榜根据用户邀请情况进行积分累加,按实际达…...

华为OD机试 - 区块链文件转储系统(Python)【2023-Q1 新题】
华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 区块链文件转储系…...

【字节面试】Fail-fast知识点相关知识点
字节面试,问到的一个小知识点,这里做一下总结,其实小编之前有一篇文章,已经对此有过涉及,不过这里知识专项针对于问题,把这个知识点拎出来说一下。 1.问题 什么是Fail-fast机制? Hashmap是否拥…...

git应用笔记(三)
在新增虚拟机linux的基础上,做git的下载与提交 1、初始化自己的用户名和邮箱。 git config --global user.name “输入你的用户名” git config --global user.email “输入你的邮箱” 2、将本地公钥及配置如图1复制粘贴到虚拟机当前用户.ssh\目录下 4929a0205f43…...

有序表的应用:设计一个增、删、查数据的时间复杂度均为O(logN)的结构
1、题目描述 设计一个结构包含如下三个方法: void add(int index, int num); //把num加入到index位置 int get(int index); //取出index位置的值(是自然序的index位置,非排序后) void remove(int index); //把index位置上的值删…...

离线环境拷贝迁移 conda envs 环境(蛮力方法,3行命令)
前言 最近要使用 GPU 服务器做实验,可惜的是,有网络连接的服务器显卡旧,算力不够;显卡较新的机器没有联网。于是有需求将旧机器上配置好的 conda 环境迁移至新机器。网上给的默认方法生成 yaml 文件迁移等 需要联网,只…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

python基础语法Ⅰ
python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器,来进行一些算术…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...

智警杯备赛--excel模块
数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中,点击确定 这是最终结果,但是由于环境启不了,这里用的是自己的excel,真实的环境中的excel根据实训…...

STM32 低功耗设计全攻略:PWR 模块原理 + 睡眠 / 停止 / 待机模式实战(串口 + 红外 + RTC 应用全解析)
文章目录 PWRPWR(电源控制模块)核心功能 电源框图上电复位和掉电复位可编程电压监测器低功耗模式模式选择睡眠模式停止模式待机模式 修改主频一、准备工作二、修改主频的核心步骤:宏定义配置三、程序流程:时钟配置函数解析四、注意…...
python数据结构和算法(1)
数据结构和算法简介 数据结构:存储和组织数据的方式,决定了数据的存储方式和访问方式。 算法:解决问题的思维、步骤和方法。 程序 数据结构 算法 算法 算法的独立性 算法是独立存在的一种解决问题的方法和思想,对于算法而言&a…...