news 2026/10/30 6:56:08

HTML表格源码实战:CSS样式、固定表头、合并单元格与响应式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HTML表格源码实战:CSS样式、固定表头、合并单元格与响应式

简介:这是一份面向 HTML 入门学习者与网页制作初学者的简单表格练习源码包,内容围绕静态页面中的表格搭建、页面呈现与素材配合展开,可用于课堂作业、基础练习,或作为快速对照表格标签与页面结构的参考。压缩包共 5 个文件,包含 1 个 html 页面源码、3 张 jpg 图片素材和 1 个 md 说明文件,整体约 30KB,体积轻便,便于直接打开源码查看结构、替换图片素材并观察页面效果。图片素材可用于还原表格页面的视觉元素,说明文档则有助于理解文件组织与使用方式。源码示例适合练习表格行列组织、边框设置、内容排版与静态素材引用等基础环节,对刚接触网页制作的人较友好。目前已有 203 人学习下载,可作为教学演示、作业参考或自行修改扩展示例的起点。

1. 三种不同的表格外观:这份 HTML 表格源码包解决什么问题

写过后台页面的人多半遇过这个场面:同一个系统里,订单列表的表格是双线边框,用户列表改成了单线,导出报表又变成没有竖线还带个灰底表头,三个页面三套样式,改一个字段宽度要翻三个文件。这份「html制作简单的表格源码.zip」就是针对这个场景——它把 HTML 表格从最朴素的 table 骨架,到斑马纹、悬停高亮、固定表头、合并单元格、窄屏横向滚动,拆成几个独立页面,一个页面只讲清一件事,样式集中在一份 css 里。刚接触前端、需要在页面里放一张能看的表格的人可以直接抄;已经写过几轮后台、想把「表格长什么样」这件事统一起来的人,也能从里面挑出可复用的写法。

2. 拆包看结构:HTML 表格源码包的文件分层与技术底座

拿到压缩包先别急着双击 index.html,先花两分钟把目录扫一遍。一个能反复用的表格源码包,价值不在于「有表格」,而在于它把结构(HTML)、表现(CSS)、行为(少量 JS)分开放了,后面你要加一列、换一套配色、接一份接口数据,改动都落在单个文件里,不会牵一发动全身。这一章按目录、HTML 骨架、CSS 底座三层往下拆。

2.1 目录长什么样:每个文件负责哪一段

这类源码包常见做法是按「示例页 + 公共资源」组织,示例页互相独立、公共样式抽出来。按这个思路,解压后大致是下面这个结构,不同打包版本的文件名可能略有出入,但职责划分基本一致:

html-table/ ├── index.html 基础表格:caption、thead、tbody、斑马纹 ├── fixed-header.html 固定表头 + 表体纵向滚动 ├── merged-cells.html colspan / rowspan 合并单元格示例 ├── responsive.html 窄屏横向滚动 + 最小宽度控制 ├── assets/ │ ├── css/ │ │ ├── reset.css 只做盒模型和默认边距归零 │ │ └── table.css 表格样式主文件,示例页共用 │ ├── js/ │ │ └── table.js 少量行为:行高亮、简单排序、数据渲染 │ └── img/ └── README.md 各示例页说明与改动指引

值得留意的是 reset.css 和 table.css 分开这件事。reset 负责把浏览器默认的margin、padding、box-sizing抹平,让不同浏览器起点一致;table.css 只写表格相关的规则。很多人把两件事混在一个文件里,改表格样式时不小心动到了全局盒模型,翻车就是这么来的。

index.html是主线,其余三个页面各自解决一个具体问题,改的时候照着文件名找就行,不用在几千行里搜关键字。

提示:如果解压后只有一个 html 文件、样式全写在<style>标签里,也别急着放弃。把它拆成 assets/css 两个文件是十分钟的事,拆完再继续往下读,后面所有改动都会轻松很多。

2.2 最小可用的表格结构:caption、colgroup、thead、tbody 各管什么

HTML 表格最大的问题不是样式难写,是结构一开始就写歪了。用<div>拼出来的假表格看着像表格,但对齐靠手算、无障碍读不出行列关系、复制到 Excel 里全乱。真正的 table 有一整套语义标签,各管一段,写对了后面样式才有的放矢。

<!-- 一份完整的表格骨架,每一层都有明确职责 --> <table class="data-table"> <!-- 表格标题,会被读屏软件优先朗读,也是视觉上的一行说明 --> <caption>2024 年第一季度各区域销售明细</caption> <!-- 列定义:只声明列数和列宽,通常配合 table-layout: fixed 使用 --> <colgroup> <col class="col-region"> <!-- 第 1 列:区域 --> <col class="col-person"> <!-- 第 2 列:负责人 --> <col class="col-amount"> <!-- 第 3 列:金额,右对齐 --> <col class="col-rate"> <!-- 第 4 列:完成率 --> </colgroup> <!-- 表头区:一行 th,scope="col" 告诉读屏软件这是一列的标题 --> <thead> <tr> <th scope="col">区域</th> <th scope="col">负责人</th> <th scope="col" class="num">金额(万元)</th> <th scope="col" class="num">完成率</th> </tr> </thead> <!-- 表体区:数据行,一行 tr 内放若干 td --> <tbody> <tr> <th scope="row">华东</th> <!-- 行首用 th + scope="row",表示这一行的标题 --> <td>某同学</td> <td class="num">128.50</td> <td class="num">92%</td> </tr> <tr> <th scope="row">华南</th> <td>某开发者</td> <td class="num">96.20</td> <td class="num">88%</td> </tr> </tbody> <!-- 表尾区:常放合计行,浏览器打印时会在每页重复 --> <tfoot> <tr> <th scope="row" colspan="2">合计</th> <td class="num">224.70</td> <td class="num">—</td> </tr> </tfoot> </table>

逐层说清楚。caption是表格标题,不是可有可无的装饰,它是表格和正文之间的桥梁,读屏软件进入表格前会先读它。colgroup里的<col>本身不显示内容,只承载列级样式,在table-layout: fixed模式下,给 col 设宽度等于给整列设了宽度,比在每行第一个 td 上写 width 靠谱得多。

thead/tbody/tfoot三个分区不只是语义,还带实际行为:打印时 thead 会在每页顶部重复,tfoot 在每页底部重复;position: sticky做吸顶也通常挂在这三个分区里的单元格上。scope="col"和scope="row"是给辅助技术用的,写起来不费劲,但表头和数据列一多,没这两个属性读屏体验会立刻崩塌。

参数上唯一需要记的是colspan和rowspan:前者横向合并几列,后者纵向合并几行。注意合计行里colspan="2",意味着后面只剩两个 td 可选,这一行的总列数必须凑够 4,少一个多一个都会让整张表的列错位——这是最高频的翻车点,第 5 章会细讲。

2.3 样式底座:border-collapse 与 table-layout 决定后面所有坑

结构写对之后,样式只需要两条属性决定大局:border-collapse和table-layout。这两条选错,后面怎么调都是玄学。

/* assets/css/table.css —— 表格样式的两大底座 */ /* 1. 边框合并:相邻单元格的边框合成一条,避免双线 */ .data-table { border-collapse: collapse; /* 固定表格布局:列宽由第一行或 colgroup 决定,不再随内容伸缩 */ table-layout: fixed; width: 100%; /* 给单元格边框一个统一来源,后面 td 只画右边和下边 */ border: 1px solid #e5e7eb; font-size: 14px; } /* 固定布局下必须显式给列宽,否则浏览器会均分 */ .data-table .col-region { width: 20%; } .data-table .col-person { width: 24%; } .data-table .col-amount { width: 28%; } .data-table .col-rate { width: 28%; } /* 单元格:只画右、下边框,左、上由表格外层和上一格补齐,从根上避免双线 */ .data-table th, .data-table td { padding: 10px 12px; border-right: 1px solid #e5e7eb; border-bottom: 1px solid #e5e7eb; text-align: left; /* 长文本超出时省略,配合固定布局才不会把列撑开 */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 数字列右对齐,这是表格可读性的基本功,小数点能对齐 */ .data-table .num { text-align: right; font-variant-numeric: tabular-nums; /* 等宽数字,列对齐更整齐 */ }

border-collapse: collapse的作用是让相邻单元格共享一条边框,默认值separate下每个 td 各画各的,两条 1px 线并排就成了 2px 的粗线,这就是「双线」的来源。配合「只画一边」的画法,即使将来把 collapse 换成 separate,也不会突然冒出双线。

table-layout: fixed是另一个分水岭。默认的auto布局下,浏览器要先算完所有内容宽度再分配列宽,结果就是某一列塞了个长链接,整张表被撑歪、其他列被压扁。改成 fixed 后列宽只看 colgroup 或第一行的设定,内容超了走overflow: hidden加省略号,表格宽度从此稳定。代价是必须显式给列宽,不给就均分——这也是新手改完 fixed 觉得「列宽不听使唤」的真实原因,不是属性失灵,是没配宽度。

注意:table-layout: fixed和white-space: nowrap是一对。只开 fixed 不开 nowrap,长文本会折行把行高撑得很高;只开 nowrap 不开 fixed,长文本会把列硬生生撑宽。两个一起用,表格外观才可预期。

到这里,结构层和样式底座就齐了。后面三章的所有效果——斑马纹、吸顶、合并、窄屏滚动——都建立在这套骨架上,骨架不对,效果怎么加都不稳。

3. 核心代码拆解:从静态表格到可维护表格的四段实现

上一章把结构和底座搭好了,这一章处理四个实战里绕不开的效果。每段都是独立的,可以拆开抄:斑马纹负责可读性,吸顶负责长表浏览,合并单元格负责报表类排版,横向滚动负责窄屏兼容。四段的共同点是都用 CSS 完成,几乎没有 JS 参与,这也是静态表格源码包最值得抄的地方——能用样式解决的,别上脚本。

3.1 斑马纹与悬停:用选择器而不是手写 class

最早写表格的人喜欢在 HTML 里手动给偶数行加class="even",数据一多、增删一行就全乱。正确做法是交给:nth-child,让浏览器自己数行。

/* 斑马纹:只对 tbody 内的行生效,不污染表头和表尾 */ .data-table tbody tr:nth-child(even) { background-color: #f9fafb; } /* 悬停高亮:优先级要压过斑马纹,所以写在后面,且选择器更具体 */ .data-table tbody tr:hover { background-color: #eef4ff; /* 悬停时给整行一个左侧色条,扫视长表时定位更快 */ box-shadow: inset 3px 0 0 #3b82f6; } /* 选中行:给 JS 加 .is-selected 后使用,覆盖悬停色 */ .data-table tbody tr.is-selected { background-color: #e0edff; } /* 表头:单独一套底色,和斑马纹区分开 */ .data-table thead th { background-color: #f3f4f6; font-weight: 600; color: #111827; /* 表头文字不让换行,列名短且固定 */ white-space: nowrap; }

这里有三个细节值得留意。第一,斑马纹只挂在tbody tr上,如果把选择器写成.data-table tr:nth-child(even),表头那一行也会被算进去,奇偶就错位了。第二,:hover规则必须写在:nth-child之后,同等优先级下后写的胜出,否则鼠标移上去偶数行纹丝不动。第三,box-shadow: inset画左边条比border-left好,因为它不占布局空间,行高不会因为悬停而跳动——这类 1px 的抖动肉眼很敏感。

参数上,底色建议只取两种灰度差 3%~5%,差值太大整张表像斑马线,反而干扰阅读。数字列配font-variant-numeric: tabular-nums,让每个数字宽度一致,右对齐时小数点能对齐成一列,这是报表类表格最容易被忽略、却最影响观感的一条。

3.2 表头吸顶:sticky 的写法和它的三个前提

长表格往下滚,表头跟着滚走,看第五十行时已经忘了哪列是金额。position: sticky能解决,但它有个前提条件组,缺一个就完全没反应,很多人第一次用都栽在这。

<!-- fixed-header.html:滚动容器包住表格,sticky 才有参照物 --> <div class="table-scroll"> <table class="data-table"> <thead> <tr> <th scope="col">区域</th> <th scope="col">负责人</th> <th scope="col" class="num">金额(万元)</th> </tr> </thead> <tbody> <!-- 这里放足够多的行,才能滚起来看出效果 --> <tr><th scope="row">华东</th><td>某同学</td><td class="num">128.50</td></tr> <tr><th scope="row">华南</th><td>某开发者</td><td class="num">96.20</td></tr> <!-- ... --> </tbody> </table> </div>
/* 滚动容器:必须有一个明确的高度上限和 overflow,sticky 才知道往哪粘 */ .table-scroll { max-height: 420px; overflow-y: auto; /* 横向也一起管住,后面窄屏那节会复用这个容器 */ overflow-x: auto; } /* 表头单元格吸顶 */ .table-scroll thead th { position: sticky; top: 0; z-index: 2; /* 压住表体内容 */ background-color: #f3f4f6; /* 必须有不透明背景,否则内容会从下面透出来 */ /* 吸顶后原来的下边框会被单元格裁掉一半,用 box-shadow 补一条线 */ box-shadow: inset 0 -1px 0 #e5e7eb; }

三个前提说清楚。一,滚动容器要能滚:.table-scroll必须有max-height或height,只写overflow-y: auto而高度由内容撑开,容器永远不需要滚动,sticky 自然不动。二,sticky 元素不能是滚动容器的直接子元素以外的奇怪结构:标准做法就是 th 的某个祖先承担滚动,th 本身粘在这条滚轴上,中间不要插overflow: hidden的包装层,那会把滚轴切断。三,背景必须不透明:透明的表头滚过去时,表体文字会从表头下方穿出来,看着像重影,实际是背景没设。

还有个容易忽略的点:吸顶后原有border-bottom会被裁掉(粘贴定位的元素边框参与滚动的表现不稳定),改用box-shadow: inset 0 -1px 0补线,视觉上才连续。z-index也要给,表体里如果有定位元素,表头会被压下去。

3.3 合并单元格:colspan 与 rowspan 的计数规则

合并单元格是报表排版的必需品,也是错位的高发区。规则只有一条,但必须记死:同一行里,每个单元格占的列数(colspan 的值)加起来,必须等于这张表的列数。rowspan 是从当前行往下占行,被占掉的位置在下面几行里要空出来。

<!-- merged-cells.html:季度汇总表的典型合并 --> <table class="data-table"> <caption>分区域季度完成情况</caption> <thead> <tr> <!-- 第一列跨两行:区域这个名字同时管上面两行表头 --> <th scope="col" rowspan="2">区域</th> <!-- 横向跨两列:把「上半年」盖住 Q1、Q2 两个子列 --> <th scope="col" colspan="2">上半年</th> <th scope="col" rowspan="2" class="num">全年目标</th> </tr> <tr> <!-- 这一行只剩三个格子可选,因为「区域」被上一行的 rowspan 占掉了 --> <th scope="col" class="num">Q1</th> <th scope="col" class="num">Q2</th> </tr> </thead> <tbody> <tr> <th scope="row" rowspan="2">华东</th> <!-- 华东跨两行,下面那行不用再写区域 --> <td class="num">60.10</td> <td class="num">68.40</td> <td class="num" rowspan="2">300.00</td> <!-- 全年目标也跨两行 --> </tr> <tr> <!-- 这一行只需填 Q1、Q2 之外的增量数据,区域和目标已被上行占位 --> <td class="num">12.30</td> <td class="num">9.80</td> </tr> </tbody> </table>

数一遍第一行:区域(占 1 列,跨 2 行)+ 上半年(占 2 列)+ 全年目标(占 1 列)= 4 列,和表体一致。第二行里「区域」和「全年目标」的位置被上一行的 rowspan 占了,所以只能写 Q1、Q2 两个 th,加起来正好 2 列——注意这里不是 4 列,因为被占的两列不参与本行计数。这就是最容易数错的地方:colspan 相加时,要把上一行 rowspan 留下来的空位算作已占列。

参数上,rowspan的值是「从当前行开始向下占几行」,写 2 表示当前行加下面一行。合并之后被合并的单元格必须从 HTML 里删掉,不能留着隐藏,留着就会多出一列。第 5 章会专门讲这个错位现象怎么排查。

3.4 窄屏不炸:overflow 包裹层与 min-width 的配合

表格在手机上是天然的灾难:列一多,整页被撑出横向滚动条,或者列被压成一条缝。最省事也最稳的方案不是把表格改成卡片,而是给表格套一个横向滚动容器。

/* responsive.html 的关键三行 */ .table-wrap { width: 100%; overflow-x: auto; /* 内容超宽时容器自己滚,不带动整页 */ -webkit-overflow-scrolling: touch; /* 移动端惯性滚动 */ } /* 表格本身给一个最小宽度,保证列不会被压扁 */ .table-wrap .data-table { min-width: 720px; /* 窄屏下取消固定布局,让内容有机会自己决定宽度 */ table-layout: auto; } /* 小屏媒体查询:隐藏次要列,减少横向滚动距离 */ @media (max-width: 640px) { .data-table .col-person, .data-table th:nth-child(2), .data-table td:nth-child(2) { display: none; /* 负责人列在手机上不展示 */ } .table-wrap .data-table { min-width: 480px; } }

逻辑很直白:外层容器负责「滚」,表格负责「宽」。min-width: 720px是经验值,四到六列的表格在 720px 以下就开始挤了,低于这个宽度就让它滚,别硬压。隐藏次要列是第二层优化,用:nth-child配合 colgroup 的列序,隐藏后记得同步调整 min-width,否则会留一片空白。

要注意overflow-x: auto必须加在包裹层,不能加在 table 上——table 是display: table,它的 overflow 行为不一致,直接加在 table 上多数浏览器不生效。另外如果表格里同时用了 sticky 表头,包裹层就复用 3.2 节那个.table-scroll,两个方向一起管,别套两层容器。

4. 运行与验收:打开方式与逐项自检清单

源码包拿到手,很多人第一反应是双击 index.html,看到表格出来了就算跑通。能显示和能用是两回事,这一章把打开方式和验收项列清楚,照着走一遍,能提前发现八成的问题。

4.1 双击打开和起本地服务的差别

纯静态的 HTML+CSS 表格,双击打开确实能看,file://协议下样式和结构都正常。但只要文件里出现了fetch拉数据、或者用了 ES module 的import,双击就会直接报跨域错误,控制台一片红。

# 在解压后的目录里起一个静态服务,避免 file:// 的各种限制 # Python 3 自带,不用额外装东西 python -m http.server 8080 # 如果有 Node 环境,用 npx 更快,不污染全局 npx serve . -l 8080

命令跑起来之后浏览器访问http://localhost:8080/index.html。两条命令任选一条,前者依赖系统里的 Python,后者依赖 Node,都没有的话,VS Code 装个 Live Server 插件右键打开也行。起服务的额外好处是改了 css 保存后刷新就能看到,不会因为浏览器缓存file://路径而看到旧样式。

参数上,端口挑 8080、3000、5173 这类不冲突的就行,被占用时换个数字。目录要停在包含 index.html 的那一层,起在上一级会导致路径全错。

提示:如果打开后样式完全没生效,先按 F12 看 Network 面板里 table.css 是不是 404。压缩包里带的相对路径如果和你解压后的目录层级不一致,最容易出现这个问题。

4.2 一份能照着点的验收清单

表格这种东西,肉眼扫一遍很容易漏,按下面几项逐条过,比反复刷新靠谱。

检查项怎么看不合格的表现
列数一致性数 thead 第一行的 colspan 之和,和 tbody 每行对比某一行整体右移一格
边框放大到 200% 看单元格交界交界处出现 2px 粗线
列宽稳定往某个 td 里粘一段长文本整张表被撑宽、其他列变窄
表头吸顶滚动表体到中间表头跟着滚走,或吸住但内容透出来
斑马纹数第 2、4、6 行表头也被染了色,或缺一行
窄屏开发者工具切到 375px 宽整页出现横向滚动条
打印Ctrl+P 预览表头只在第一页出现,或行被从中间截断

最后一项打印最容易被跳过,但真到导出报表时才发现表头不重复就晚了。补一条打印样式基本能解决:

@media print { /* 打印时取消容器的高度限制和滚动,让整张表完整铺开 */ .table-scroll { max-height: none; overflow: visible; } /* 表头在每页顶部重复 */ thead { display: table-header-group; } /* 单行不要被分页从中间切断 */ tr { break-inside: avoid; } /* 打印时去掉交互态颜色,省墨也清晰 */ .data-table tbody tr:hover { background: transparent; box-shadow: none; } }

display: table-header-group是让 thead 重复的关键,浏览器对 thead 的默认处理在打印时并不总是重复。break-inside: avoid防止一行数据被劈成两半,长表格打印出来会整齐很多。

5. 常见问题排查:表格错位、边框双线、吸顶失效的 5 个坑

表格的坑有个共同特征:现象看着五花八门,原因都落在前面几章讲的那几条属性上。下面五条是我自己踩过、也在别人代码里反复见到的,按「现象 → 原因 → 解决」写,对着排查基本能定位。

5.1 坑一:单元格交界处出现 2px 粗线

现象:把页面放大到 200%,能明显看到相邻单元格之间是一条粗线,和外围边框粗细不一致,整体像格子布。

原因:table 上没写border-collapse: collapse,浏览器走默认的separate模式,每个 td 各画各的边框,两条 1px 并排视觉上就是 2px。另一种情况是写了 collapse,但同时又给 table 加了border-spacing,两者冲突。

解决:table 上补border-collapse: collapse,并且把 td 的边框改成只画右边和下边(见 2.3 节的写法),让每条线只有一个来源。如果确实需要separate模式来做圆角或间距,那就给 td 用border-right、border-bottom,再用 table 的border-left、border-top补齐外框,不要四个方向都写。

5.2 坑二:列宽怎么调都不听话

现象:colgroup 里明明写了width: 20%,页面上某一列还是被内容撑得很宽,或者所有列宽完全一样。

原因:两个可能。一是没开table-layout: fixed,auto 布局下浏览器优先满足内容宽度,col 上的 width 只是建议值。二是开了 fixed 但 colgroup 的<col>数量和实际列数对不上,或者某一行的单元格因为 colspan 导致列数和 col 数不一致,浏览器放弃按 col 分配。

解决:先确认table-layout: fixed已生效(在开发者工具里看 computed 面板),再数一遍 colgroup 里 col 的个数是否等于表格列数。开了 fixed 之后必须每一列都有明确宽度,缺一列浏览器会均分剩余空间,表现出来就是「有的列宽有的列一样宽」。

5.3 坑三:sticky 表头完全不生效

现象:给 th 写了position: sticky; top: 0,滚动时表头纹丝不动地跟着跑,或者只在某些页面生效。

原因:sticky 需要一个「能滚的祖先」。常见三种情况:包装容器只有overflow: auto没有高度限制,容器被内容撑开所以永远不滚;中间多套了一层overflow: hidden的 div,把滚动上下文截断了;祖先元素上有transform、filter、perspective,创建了新的包含块,sticky 的参照系跑偏。

解决:给滚动容器设max-height并确认它真的出现了滚动条;把中间多余的overflow: hidden包装层拆掉;检查祖先链上有没有动画用的transform,有就挪到别的元素上。另外表头背景必须是不透明色,否则吸住后内容会透出来,看着像「没生效」。

5.4 坑四:手机上整页被表格撑出横向滚动

现象:桌面端正常,切到手机尺寸后整个页面能左右拖,导航栏和页脚也跟着飘。

原因:表格的固有宽度超过了视口,但没有包裹层去承接这个溢出,浏览器只能把溢出算到最近的块级祖先上,通常是 body,于是整页滚动。直接给 table 加overflow-x: auto也不管用,因为 table 的 display 是 table,overflow 行为不符合预期。

解决:外面套一层.table-wrap { overflow-x: auto; width: 100%; },表格本身给min-width。如果表格里还有 sticky 表头,包裹层同时承担纵向滚动,一个容器管两个方向。隐藏次要列时用:nth-child配合 colgroup 的列序,隐藏后把 min-width 调小,避免留白。

5.5 坑五:合并单元格之后整行右移

现象:某一行数据整体往右错了一格,和表头对不上,越往下越乱。

原因:colspan 和 rowspan 的计数算错了。两种情况最多:一是在有 colspan 的行里,单元格数量还是按原始列数写,多出来的格子把行撑宽;二是上一行用了 rowspan,下一行没有把被占的位置空出来,照常写满格子,导致下一行多占了一列。

解决:按 3.3 节的规则重数一遍,每行的colspan之和必须等于总列数,rowspan 占掉的位置在后续行里不写单元格。排查时最快的办法是临时给每个 td 加outline: 1px solid red,一眼就能看出哪一行的格子多出来或者少一块。改完记得把 outline 删掉。

6. 二次开发:把静态表格改成配置驱动渲染

源码包里的示例页是写死的 HTML,行数固定、列固定。真接到项目里,数据来自接口、列数会变、还要能动态加一列「操作」,手写 tr 就不现实了。我一般的改法是把「列」抽成配置数组,用一个渲染函数把数据灌进去,HTML 里只留一个空 table 当挂载点。

// assets/js/table.js —— 配置驱动的表格渲染 // 列配置:改列、加列、调顺序、控对齐,全在这一个数组里 const columns = [ { key: 'region', title: '区域', align: 'left', width: '20%' }, { key: 'owner', title: '负责人', align: 'left', width: '24%' }, { key: 'amount', title: '金额(万元)', align: 'right', width: '28%', format: (v) => v.toFixed(2) }, // 数字列统一保留两位 { key: 'rate', title: '完成率', align: 'right', width: '28%', format: (v) => `${(v * 100).toFixed(0)}%` } ]; // 渲染函数:接收数据数组,重建 thead 和 tbody function renderTable(tableEl, columns, rows) { // 表头:按 columns 顺序生成 th,宽度写到 style 上,配合外层 fixed 布局 const thead = tableEl.querySelector('thead'); thead.innerHTML = `<tr>${columns.map((col) => `<th scope="col" style="width:${col.width};text-align:${col.align}">${col.title}</th>` ).join('')}</tr>`; // 表体:逐行逐列取值,没值的渲染成短横线占位,保持列对齐 const tbody = tableEl.querySelector('tbody'); tbody.innerHTML = rows.map((row) => { const tds = columns.map((col) => { const raw = row[col.key]; const text = raw == null ? '—' : (col.format ? col.format(raw) : String(raw)); return `<td style="text-align:${col.align}">${escapeHtml(text)}</td>`; }).join(''); return `<tr>${tds}</tr>`; }).join(''); } // 数据来自接口时不可信,拼字符串前先转义,防止内容里的标签被执行 function escapeHtml(str) { return str.replace(/[&<>"']/g, (ch) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[ch])); } // 使用:HTML 里只留 <table class="data-table"><thead></thead><tbody></tbody></table> const tableEl = document.querySelector('.data-table'); renderTable(tableEl, columns, [ { region: '华东', owner: '某同学', amount: 128.5, rate: 0.92 }, { region: '华南', owner: '某开发者', amount: 96.2, rate: 0.88 } ]);

改动点集中在columns数组:加一列就在数组里加一项,改宽度改width,改对齐改align,需要格式化就挂format函数。表头宽度直接写到 th 的 style 上,外层 table 保持table-layout: fixed,列宽依旧稳定。

验收方法很土但有效:把渲染前后生成的 DOM 在开发者工具里展开,数一遍<th>的数量和每行<td>的数量是否一致,再检查有没有哪个colspan打乱了计数。如果服务端返回的字段名和 columns 里的 key 对不上,页面会出现整列短横线——这种情况先看接口返回的 JSON,别急着怀疑渲染函数。

有个血泪教训:escapeHtml一开始我嫌麻烦没加,结果某个字段里带了&,渲染出来后面文字全变样,排查了半天才想起是拼接未转义。从那以后我每次做字符串拼 DOM 都强制先过一遍转义函数,哪怕数据是自己准备的。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 6:55:34

TS转MP4实战:FFmpeg与图形工具两种方法详解

TS 文件这个格式&#xff0c;做流媒体和音视频处理的人应该都不陌生。你从各种录制工具、直播缓存、监控设备里导出来的视频&#xff0c;十有八九是.ts后缀。文件能播&#xff0c;但拖进剪辑软件不认&#xff0c;发给别人对方打不开&#xff0c;上传到某些平台又提示格式不支持…

作者头像 李华
网站建设 2026/10/30 6:54:36

让AI合并两张表,销售额怎么翻倍了?从一个重复订单号讲透数据关联

合并脚本没有报错&#xff0c;销售额却从600元变成1200元&#xff0c;问题可能出在关联粒度。本文用可运行的Python示例&#xff0c;拆解一对多、多对多、重复键和订单金额重复累加&#xff0c;给出主键检查、未匹配清单与逐单核验&#xff0c;并说明为什么总额一致也不等于结果…

作者头像 李华
网站建设 2026/10/30 6:52:57

ARM Compiler 5.05安装与验证:从环境配置到最小工程实战

简介&#xff1a;Arm Compiler 5.05 update 1&#xff08;build 106&#xff09;是面向嵌入式开发者的经典 ARM 编译器安装包&#xff0c;适用于使用 Keil MDK 进行 ARM 平台工程开发、需要指定旧版编译工具链的中高级开发者&#xff0c;可解决新版编译器兼容性不足或工程需固定…

作者头像 李华
网站建设 2026/10/30 6:52:40

第十篇:《全文检索:tsvector、tsquery 与中文分词》

在上一篇文章中&#xff0c;我们通过分区表实现了“分而治之”——查询只扫描目标分区&#xff0c;旧数据用 DETACH 而非 DELETE 来清理。但分区解决的是数据规模问题&#xff0c;而不是搜索质量问题。当用户输入“数据库性能”时&#xff0c;如果底层用的是 LIKE ‘%数据库性能…

作者头像 李华
网站建设 2026/10/30 6:52:32

【Python】集合_数学运算

并集 交集 差集 对称差集 并集 A {1,2,3,4,5,6} B {4,5,6,7,8,9}交集差集对称差集 属于A集合&#xff0c;而且属于B集合&#xff0c;但是不能同时属于A集合和B集合

作者头像 李华
网站建设 2026/10/30 6:51:34

731天共享单车数据集 | 日级+小时级 | 需求预测实操 | 机器学习

封面&#xff1a;慧数研析社原创 AI 插画。正文曲线与误差图为真实数据计算图&#xff0c;不是官方效果展示。 共享单车运力安排涉及两个不同的问题&#xff1a;明天整体会有多少次租赁&#xff0c;以及某个小时会出现多大的需求。前者帮助理解日常资源规模&#xff0c;后者关…

作者头像 李华