当前位置: 首页 > news >正文

手撸一个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组件这个文章我一直都想写&#xff0c;今天终于得空来写它了。小编认为Table组件是组件库里"较为复杂"的一个组件&#xff0c;因为它的扩展性非常强&#xff0c;并且它的基础样式如何去写都非常考究&#xff0c;那么今天我就带大家来实现一个基础…...

Python|Leetcode刷题日寄Part01

Python|Leetcode刷题日寄Part0101&#xff1a;两数之和02&#xff1a;无重复字符的最长子串03&#xff1a;两数相加04&#xff1a;反转链表05&#xff1a;有效的括号06&#xff1a;回文数07&#xff1a;删除有序数组中的重复项08&#xff1a;删除链表的倒数第N个结点09&#xf…...

微信小程序更改头像昵称

背景 前面写了一篇关于小程序头像昵称获取更改的方案&#xff0c;有很多小伙伴私信我发一个整体的逻辑思路&#xff01; 解决思路 前面的这篇文章中我们给出了页面中获取头像昵称的代码&#xff1a; <view class"headInfo" data-weui-theme"{{theme}}&qu…...

Linux 基础知识之文件系统

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

LeetCode 36. 有效的数独

LeetCode 36. 有效的数独 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 请你判断一个 9x99 x 99x9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 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&#xff1a;存储区域网络 IP SAN&#xff1a;以太网交换机和普通网线连接的存储&#xff0c;交换机之间做堆叠FC SAN&#xff1a;FC&#xff08;光纤&#xff09;交换机和光纤连接的存储&#xff0c;交换机之间做级联Server SAN&#xff1a;可以使用以太网交换机…...

说说 React 中 fiber、DOM、ReactElement、实例对象之间的引用关系

原生组件 fiber 原生组件 fiber&#xff0c;指的就是 type 为 “span”、“div” 的 fiber。 1.fiber.stateNode 指向真实 DOM 节点&#xff1b;2.node["__reactFiber$" randomKey] 指向对应 fiber&#xff0c;使用随机数是防止和业务代码的属性名冲突&#xff0c;…...

LaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式)

文章目录 LaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式)refnotedemoLaTex公式使用(Word中的公式编辑,尤其是方程组等联合公式) ref markdown中公式编辑教程 在 Microsoft Word 中使用 LaTeX 输入数学公式【比较全,介绍了支持的语法和不支持的语法】 用wo…...

S5P6818_系统篇(2)源码编译及烧录

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

LDPC码的编译码原理简述

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

网络安全——数链路层据安全协议

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

spring的启动过程(一) :IOC容器的启动过程

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

这次,我的CentOS又ping不通www.baidu.com了(gateway配置)

当我们保证了宿主机与虚拟机的ip地址在同一网段&#xff0c;并且我们使用虚拟机ping宿主机&#xff0c;与宿主机ping虚拟机都可以互相ping通的情况下虚拟机却ping不通外网了&#xff0c;由于涉及到了跨越网络访问&#xff0c;所以我们应该把问题聚焦在网关的配置上&#xff01;…...

启智社区“我为开源狂”第六期活动小白教程之基础活跃榜

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

华为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知识点相关知识点

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

git应用笔记(三)

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

有序表的应用:设计一个增、删、查数据的时间复杂度均为O(logN)的结构

1、题目描述 设计一个结构包含如下三个方法&#xff1a; void add(int index, int num); //把num加入到index位置 int get(int index); //取出index位置的值&#xff08;是自然序的index位置&#xff0c;非排序后&#xff09; void remove(int index); //把index位置上的值删…...

离线环境拷贝迁移 conda envs 环境(蛮力方法,3行命令)

前言 最近要使用 GPU 服务器做实验&#xff0c;可惜的是&#xff0c;有网络连接的服务器显卡旧&#xff0c;算力不够&#xff1b;显卡较新的机器没有联网。于是有需求将旧机器上配置好的 conda 环境迁移至新机器。网上给的默认方法生成 yaml 文件迁移等 需要联网&#xff0c;只…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...