一种更精细的HTML表格斑马色设置方法

如图,让HTML表格中的任意一个单元格都与其上方的相邻单元格呈现不同的底纹,在视觉上远远好过仅用CSS的"tr:nth-child(2n+1) { background-color:#eee}"进行设置。但是要实现上面这种斑马色设置,仅靠CSS没法实现,仅用JS+CSS实现也不优雅,最佳实现方式是先在HTML中对原子行进行语义标记(所谓原子行即上图中6~8列这种不存在合并行的行),再结合 JS + CSS 进行处理。示例文件如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<title>中国帝王简表</title>
	<style>
		* {
			box-sizing: border-box;
		}

		body {
			font-family: '霞鹜文楷红楼梦', system-ui, 'Segoe UI', 'Roboto', 'Noto Sans', '微软雅黑', sans-serif;
			background-color: #f5f7f0;
			margin: 0;
			padding: 20px;
			color: #1e2a1f;
		}

		.table-container {
			max-width: 100%;
			border-radius: 8px;
			background: white;
			box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
			overflow: hidden;
			border: 1px solid #cbd5b0;
		}

		/* 关键: 滚动区域 + 固定表头 */
		.scrollable-table-wrapper {
			overflow-y: auto;
			overflow-x: auto;
			max-height: 40em;
			/* 大约30行的高度(按行高约1.8em算,30行≈54行但实际自适应,使用em确保约30行内容可见) */
			/* 为了让滚动条精确控制在30行左右,同时保证标题始终可见,使用max-height并配合行高 */
			scrollbar-width: thin;
		}

		/* 确保表格布局稳定 */
		.fixed-header-table {
			width: 100%;
			border-collapse: collapse;
			font-size: 0.9rem;
			min-width: 1100px;
			/* 保证宽表格可横滚,内容清晰 */
			background-color: #fff;
		}

		/* 表头样式: 粘性定位 + 背景色 + 阴影提升层次感 */
		.fixed-header-table thead tr {
			background-color: #2c5e2a;
			color: white;
		}

		.fixed-header-table th {
			position: sticky;
			top: 0;
			background-color: #2c5e2a;
			color: white;
			border: 1px solid #4a7843;
			padding: 12px 8px;
			font-weight: 600;
			font-size: 0.85rem;
			letter-spacing: 0.3px;
			text-align: center;
			vertical-align: middle;
			/* white-space: nowrap; */
			z-index: 10;
			box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
		}

		/* 普通单元格样式 */
		.fixed-header-table td {
			border: 1px solid #cbd5b0;
			padding: 8px 6px;
			text-align: center;
			/* 水平居中 */
			vertical-align: middle;
			/* 竖直居中 */
			background-color: #ffffff;
			font-size: 0.85rem;
		}

		/* 备注列左对齐 */
		.fixed-header-table td:nth-child(9) {
			text-align: left !important;
			word-break: break-word;
			max-width: 260px;
		}

		/* 其他列确保可读 */
		.fixed-header-table td:nth-child(5),
		/* 在位时间 */
		.fixed-header-table td:nth-child(7) {
			/* 年号使用时间 */
			white-space: nowrap;
			max-width: 20em;
		}

		th:nth-child(1) {
			min-width: 6em;
			width: 5%;
		}

		/* 国号 */
		th:nth-child(2) {
			min-width: 5em;
			width: 9%;
		}

		/* 庙号 */
		th:nth-child(3) {
			min-width: 5em;
			width: 9%;
		}

		/* 谥号 */
		th:nth-child(8) {
			max-width: 3em;
			width: 9%;
		}

		/* 干支 */
		th:nth-child(9) {
			min-width: 10em;
			width: 24%;
		}

		/* 备注列加宽 */

		/* 奇偶行设置斑马色,提高可读性 */		
		td.odd,
		th.odd {
			background-color: #e6f3ff;
		}
        /* 其实只需要设置 odd 或 even 之一就足以分别,这里还是全设算了*/
		td.even,
		th.even {
			background-color: #f9f9f9;
		}

		/* 可选:让合并单元格颜色更明显或统一 */
		td[rowspan] {
			vertical-align: middle;
		}

		.fixed-header-table tbody tr:hover td {
			background-color: #633;
			color: white;
			transition: 0.1s;
		}

		/* 头部说明和标题区域 */
		.page-title {
			text-align: center;
			margin: 0 0 12px 0;
			font-size: 1.7rem;
			font-weight: 600;
			color: #1f3b1a;
			letter-spacing: 1px;
		}

		.note {
			font-size: 0.85rem;
			background: #e9f3e3;
			padding: 10px 18px;
			border-radius: 24px;
			margin-bottom: 20px;
			color: #2c4b2a;
			text-indent: 0;
			border-left: 4px solid #4caf50;
		}

		.note a {
			color: #1e6f3f;
			text-decoration: none;
			border-bottom: 1px dotted;
		}

		.note a:hover {
			text-decoration: underline;
		}

		/* 让滚动条区域更美观 */
		.scrollable-table-wrapper::-webkit-scrollbar {
			height: 8px;
			width: 8px;
		}

		.scrollable-table-wrapper::-webkit-scrollbar-track {
			background: #e2e8d5;
			border-radius: 8px;
		}

		.scrollable-table-wrapper::-webkit-scrollbar-thumb {
			background: #6b8c5c;
			border-radius: 8px;
		}

		/* 响应式微调 */
		@media (max-width: 760px) {

			.fixed-header-table th,
			.fixed-header-table td {
				padding: 6px 4px;
				font-size: 0.75rem;
			}

			.page-title {
				font-size: 1.3rem;
			}
		}

		footer {
			margin-top: 24px;
			text-align: center;
			font-size: 0.75rem;
			color: #5f7357;
		}
	</style>
</head>

<body>
	<div class="page-title">📜 中国历代帝王"庙号·谥号·年号与公元纪年关系"简表</div>
	<div class="note">
		📌 注:有些日期因历史记载不精确或本人没有花精力查阅典籍仅给到了年或月。汉字日期为农历,阿拉伯数字日期为公历,公历与农历对照依据<a
			href="http://www.wangjiarong.com/2021/ChineseCalendar/index_simp.html" target="_blank"
			rel="noopener">王家荣万年历</a>查定。
	</div>
	<div class="table-container">
		<div class="scrollable-table-wrapper">
			<table class="fixed-header-table">
				<thead>
					<tr>
						<th>国号</th>
						<th>庙号</th>
						<th>谥号</th>
						<th>名讳</th>
						<th>在位时间</th>
						<th>年号</th>
						<th>年号使用时间</th>
						<th>即位或年号元年干支</th>
						<th>备注</th>
					</tr>
				</thead>
				<tbody>
					<tr>
						<td>秦</td><td>---</td><td>无谥号,称始皇帝</td><td>嬴政</td><td>前247年五月---前210年七月</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">甲寅</td><td>前221年吞并六国,称始皇帝。按前秦男子称呼习俗,始皇帝名讳可称政、秦王政、赵政等,不称嬴政。</td></tr>
					<tr>
						<td>秦</td><td>---</td><td>无谥号,称秦二世</td><td>赢胡亥</td><td>前209年---前207年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">壬辰</td><td>前209年为二世元年。前207年,刘邦进攻武关,赵高发动望夷宫政变逼其自刎,以庶民礼葬于宜春苑(今西安雁塔区)</td></tr>
					<tr>
						<td>秦</td><td>---</td><td>无谥号,称孺子婴</td><td>赢子婴</td><td>前207年---前207年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">甲午</td><td>前207年,赵高谋杀了秦二世后,立子婴为秦王。四十六天,刘邦大军兵临咸阳,屯兵灞上(今陕西西安市东),子婴投降刘邦。前206年,项羽进入咸阳,将子婴杀害</td></tr>
					<tr>
						<td>汉(西汉)</td><td>太祖</td><td>高皇帝</td><td>刘邦</td><td>前202年---前195年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">己亥</td><td>---</td></tr>
					<tr>
						<td>汉(西汉)</td><td>---</td><td>孝惠皇帝</td><td>刘盈</td><td>前195年---前188年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">丙午</td><td></td></tr>
					<tr>
						<td>汉(西汉)</td><td>---</td><td>---</td><td>刘恭</td><td>前188年---前184年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">癸丑</td><td></td></tr>
					<tr>
						<td>汉(西汉)</td><td>---</td><td>---</td><td>刘弘</td><td>前184年---前180年</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">丁巳</td><td></td></tr>
					<tr>
						<td rowspan="2">汉(西汉)</td><td rowspan="2">太宗</td><td rowspan="2">孝文皇帝</td><td rowspan="2">刘恒</td><td rowspan="2">前180年---前157年</td><td data-type="atom">前元</td><td data-type="atom">前179年---前163</td><td data-type="atom">壬戌</td><td rowspan="2">或说不是年号,但使用方式与后世年号无异</td></tr>
					<tr>
						<td data-type="atom">后元</td><td data-type="atom">前163年---前157</td><td data-type="atom">戊寅</td></tr>
					<tr>
						<td rowspan="3">汉(西汉)</td><td rowspan="3">---</td><td rowspan="3">孝景皇帝</td><td rowspan="3">刘启</td><td rowspan="3">前157年---前141年</td><td data-type="atom">前元</td><td data-type="atom">前157年---前150</td><td data-type="atom">甲申</td><td rowspan="3"></td></tr>
					<tr>
						<td data-type="atom">中元</td><td data-type="atom">前150年---前144</td><td data-type="atom">辛卯</td></tr>
					<tr>
						<td data-type="atom">后元</td><td data-type="atom">前144年---前141</td><td data-type="atom">丁酉</td></tr>
					<tr>
						<td rowspan="11">汉(西汉)</td><td rowspan="11">世宗</td><td rowspan="11">孝武皇帝</td><td rowspan="11">刘彻</td><td rowspan="11">前141年---前87年</td><td data-type="atom">建元</td><td data-type="atom">前140年---前135年</td><td data-type="atom">辛丑</td><td rowspan="11"></td></tr>
					<tr>
						<td data-type="atom">元光</td><td data-type="atom">前134年---前129年</td><td data-type="atom">丁未</td></tr>
					<tr>
						<td data-type="atom">元朔</td><td data-type="atom">前128年---前123年</td><td data-type="atom">癸丑</td></tr>
					<tr>
						<td data-type="atom">元狩</td><td data-type="atom">前122年---前117年</td><td data-type="atom">己未</td></tr>
					<tr>
						<td data-type="atom">元鼎</td><td data-type="atom">前116年---前111年</td><td data-type="atom">乙丑</td></tr>
					<tr>
						<td data-type="atom">元封</td><td data-type="atom">前110年---前105年</td><td data-type="atom">辛未</td></tr>
					<tr>
						<td data-type="atom">太初</td><td data-type="atom">前104年---前101年</td><td data-type="atom">丁丑</td></tr>
					<tr>
						<td data-type="atom">天汉</td><td data-type="atom">前100年---前97年</td><td data-type="atom">辛巳</td></tr>
					<tr>
						<td data-type="atom">太始</td><td data-type="atom">前96年---前93年</td><td data-type="atom">乙酉</td></tr>
					<tr>
						<td data-type="atom">征和</td><td data-type="atom">前92年---前89年</td><td data-type="atom">己丑</td></tr>
					<tr>
						<td data-type="atom">后元</td><td data-type="atom">前88年---前87年</td><td data-type="atom">癸巳</td></tr>
					<tr>
						<td rowspan="3">汉(西汉)</td><td rowspan="3">---</td><td rowspan="3">孝昭皇帝</td><td rowspan="3">刘弗陵</td><td rowspan="3">前87年---前74年</td><td data-type="atom">始元</td><td data-type="atom">前86年---前80年七月</td><td data-type="atom">乙未</td><td rowspan="3"></td></tr>
					<tr>
						<td data-type="atom">元凤</td><td data-type="atom">前80年八月---前75年</td><td data-type="atom">辛丑</td></tr>
					<tr>
						<td data-type="atom">元平</td><td data-type="atom">前74年---前74年</td><td data-type="atom">丁未</td></tr>
					<tr>
						<td>汉(西汉)</td><td>---</td><td>---</td><td>刘贺</td><td>前74年---前74年(在位27天)</td><td data-type="atom">---</td><td data-type="atom">---</td><td data-type="atom">丁未</td><td>前74年四月刘贺由霍光拥立,因欲削除霍光权力,在位27天后被废。前59年刘贺去世</td></tr>
					<tr>
						<td rowspan="7">汉(西汉)</td><td rowspan="7">中宗</td><td rowspan="7">孝宣皇帝</td><td rowspan="7">刘询</td><td rowspan="7">前74年---前49年</td><td data-type="atom">本始</td><td data-type="atom">前73年---前70年</td><td data-type="atom">戊申</td><td rowspan="7"></td></tr>
					<tr>
						<td data-type="atom">地节</td><td data-type="atom">前69年---前66年</td><td data-type="atom">壬子</td></tr>
					<tr>
						<td data-type="atom">元康</td><td data-type="atom">前65年---前62年</td><td data-type="atom">丙辰</td></tr>
					<tr>
						<td data-type="atom">神爵</td><td data-type="atom">前61年---前58年</td><td data-type="atom">庚申</td></tr>
					<tr>
						<td data-type="atom">五凤</td><td data-type="atom">前57年---前54年</td><td data-type="atom">甲子</td></tr>
					<tr>
						<td data-type="atom">甘露</td><td data-type="atom">前53年---前50年</td><td data-type="atom">戊辰</td></tr>
					<tr>
						<td data-type="atom">黄龙</td><td data-type="atom">前49年---前49年</td><td data-type="atom">壬申</td></tr>
					<tr>
						<td rowspan="4">汉(西汉)</td><td rowspan="4">高宗(后除庙号)</td><td rowspan="4">孝元皇帝</td><td rowspan="4">刘奭</td><td rowspan="4">前49年---前33年</td><td data-type="atom">初元</td><td data-type="atom">前48年---前44年</td><td data-type="atom">癸酉</td><td rowspan="4"></td></tr>
					<tr>
						<td data-type="atom">永光</td><td data-type="atom">前43年---前39年</td><td data-type="atom">戊寅</td></tr>
					<tr>
						<td data-type="atom">建昭</td><td data-type="atom">前38年---前34年</td><td data-type="atom">癸未</td></tr>
					<tr>
						<td data-type="atom">竟宁</td><td data-type="atom">前33年---前33年</td><td data-type="atom">戊子</td></tr>
					<tr>
						<td rowspan="7">汉(西汉)</td><td rowspan="7">统宗(后除庙号)</td><td rowspan="7">孝成皇帝</td><td rowspan="7">刘骜</td><td rowspan="7">前33年---前7年</td><td data-type="atom">建始</td><td data-type="atom">前32年---前29年</td><td data-type="atom">己丑</td><td rowspan="7"></td></tr>
					<tr>
						<td data-type="atom">河平</td><td data-type="atom">前28年---前25年</td><td data-type="atom">癸巳</td></tr>
					<tr>
						<td data-type="atom">阳朔</td><td data-type="atom">前24年---前21年</td><td data-type="atom">丁酉</td></tr>
					<tr>
						<td data-type="atom">鸿嘉</td><td data-type="atom">前20年---前17年</td><td data-type="atom">辛丑</td></tr>
					<tr>
						<td data-type="atom">永始</td><td data-type="atom">前16年---前13年</td><td data-type="atom">乙巳</td></tr>
					<tr>
						<td data-type="atom">元延</td><td data-type="atom">前12年---前9年</td><td data-type="atom">己酉</td></tr>
					<tr>
						<td data-type="atom">绥和</td><td data-type="atom">前8年---前7年</td><td data-type="atom">癸丑</td></tr>	
				</tbody>
			</table>
		</div>
	</div>
	<footer>表格支持横向/纵向滚动,表头固定,每页最多可见约30行数据,方便浏览全部朝代更迭。</footer>
	<script>
	// 在 HTML 中,针对刘贺、秦始皇等单行组的纪年列:
// <td data-type="data">---</td> <td data-type="data">---</td> <td data-type="data">丁未</td>

function addOddEvenClassesToTable(table) {
    if (!table) return;
    const rows = Array.from(table.querySelectorAll('tr'));
    let groupIndex = 0;   
    let globalRowIndex = 0; 

    for (let i = 0; i < rows.length; ) {
        const firstRowOfGroup = rows[i];
        groupIndex++;
        const isGroupOdd = (groupIndex % 2 === 1);
        
        let groupSpan = 1;
        Array.from(firstRowOfGroup.cells).forEach(cell => {
            groupSpan = Math.max(groupSpan, parseInt(cell.getAttribute('rowspan')) || 1);
        });

        for (let sub = 0; sub < groupSpan && (i + sub) < rows.length; sub++) {
            const currentRow = rows[i + sub];
            globalRowIndex++;
            const isRowOdd = (globalRowIndex % 2 === 1);
            
            Array.from(currentRow.cells).forEach(cell => {
                cell.classList.remove('odd', 'even');

                // 核心判定:
                // 1. 只要单元格有跨行 (rowspan > 1),它一定属于"组属性"
                // 2. 否则,看它有没有 data-type 标记。有标记则随行变,没标记则随组变。
                const hasRowSpan = (parseInt(cell.getAttribute('rowspan')) || 1) > 1;
                const isTimeline = cell.dataset.type !== undefined;

                if (hasRowSpan || !isTimeline) {
                    // 帝王元数据 (国号、名讳、备注等)
                    cell.classList.add(isGroupOdd ? 'odd' : 'even');
                } else {
                    // 纪年动态数据 (年号、纪年等)
                    cell.classList.add(isRowOdd ? 'odd' : 'even');
                }
            });
        }
        i += groupSpan;
    }
}
		// 在页面加载完成后执行
		document.addEventListener('DOMContentLoaded', () => {
			const table = document.querySelector('tbody');
			addOddEvenClassesToTable(table);
		});
		
	</script>
</body>

</html>

博文自秦始皇至清逊帝历代帝王庙号谥号及年号对应公元纪年表中的HTML文件改造成与上述示例页面显示效果一致的方法如下:

1、删除原博文中HTML文件script标签中的JS代码,改写成如下代码:

javascript 复制代码
function modifyHTML(table) {
			if (!table || !['TABLE', 'TBODY', 'THEAD'].includes(table.tagName)) {
				console.warn('modifyHTML: 参数必须是 TABLE、TBODY 或 THEAD 元素');
				return;
			}

			const rows = Array.from(table.querySelectorAll('tr'));
			rows.forEach(tr => {
				const tds =  Array.from(tr.querySelectorAll('td'));
				if (tds.length > 3) {
					tds.forEach((td, col) => {
						if(col > 4 && col < 8)
							td.setAttribute('data-type', 'atom');
					});
				} else {
					tds.forEach(td => {
							td.setAttribute('data-type', 'atom');
					});
				}
			});
}
// 在页面加载完成后执行
document.addEventListener('DOMContentLoaded', () => {
	const table = document.querySelector('tbody');
	modifyHTML(table);
	console.log(table.outerHTML);
});

2、浏览器中打开修改后的HTML文件;

3、按F12键打开浏览器控制台,复制控制台的输出内容,替换掉原博文中HTML文件中的tbody标签;

4、再次删除原博文中HTML文件script标签中的JS代码,复制本博文中HTML示例文件中的script标签的内容,粘贴至原博文中HTML文件的script标签中,即完成了全部改造。

表格斑马色设置问题终极版:

如图结构这么复杂的表格,要实现每一个单元格的背景颜色都与正上方的相邻单元格不同,代码如下(JS代码有详细注释,此代码依赖标题行无合并列):

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<title>表格行间间色示例</title>
	<style>
		* {
			box-sizing: border-box;
		}

		body {
			font-family: '霞鹜文楷红楼梦', system-ui, 'Segoe UI', 'Roboto', 'Noto Sans', '微软雅黑', sans-serif;
			background-color: #f5f7f0;
			margin: 0;
			padding: 20px;
			color: #1e2a1f;
		}

		.table-container {
			max-width: 100%;
			border-radius: 8px;
			background: white;
			box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
			overflow: hidden;
			border: 1px solid #cbd5b0;
		}

		/* 关键: 滚动区域 + 固定表头 */
		.scrollable-table-wrapper {
			overflow-y: auto;
			overflow-x: auto;
			max-height: 24em;
			scrollbar-width: thin;
		}

		/* 确保表格布局稳定 */
		.fixed-header-table {
			width: 100%;
			border-collapse: collapse;
			font-size: 0.9rem;
			min-width: 1100px;
			/* 保证宽表格可横滚,内容清晰 */
			background-color: #fff;
		}

		/* 表头样式: 粘性定位 + 背景色 + 阴影提升层次感 */
		.fixed-header-table thead tr {
			background-color: #2c5e2a;
			color: white;
		}

		.fixed-header-table th {
			position: sticky;
			top: 0;
			background-color: #2c5e2a;
			color: white;
			border: 1px solid #4a7843;
			padding: 12px 8px;
			font-weight: 600;
			font-size: 0.85rem;
			letter-spacing: 0.3px;
			text-align: center;
			vertical-align: middle;
			/* white-space: nowrap; */
			z-index: 10;
			box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
		}

		/* 普通单元格样式 */
		.fixed-header-table td {
			border: 1px solid #cbd5b0;
			padding: 8px 6px;
			text-align: center;
			/* 水平居中 */
			vertical-align: middle;
			/* 竖直居中 */
			background-color: #ffffff;
			font-size: 0.85rem;
		}


		td.odd {
			background-color: #e6f3ff;
		}

		td.even {
			background-color: #f9f9f9;
		}

		.fixed-header-table tbody tr:hover td {
			background-color: #ff8000;
			color: white;
			transition: 0.1s;
		}

	</style>
</head>

<body>
	<h1 style="text-align: center;">📜 复杂表格的上下行间色示例</h1>
	<div class="note">
		📌 目标:每一个单元格都与其上方的单元格的背景颜色不同。
	</div>
	<div class="table-container">
		<div class="scrollable-table-wrapper">
			<table class="fixed-header-table">
				<thead>
					<tr><th>H0</th><th>H1</th><th>H2</th><th>H3</th><th>H4</th></tr>
				</thead>
				<tbody>
					<tr><td>R0c0</td><td rowspan="2" colspan="2">R0c1</td><td>R0c2</td><td>R0c3</td></tr>
					<tr><td>R1c0</td><td rowspan="2">R1c1</td><td>R1c2</td></tr>
					<tr><td rowspan="2">R2c0</td><td>R2c1</td><td>R2c2</td><td rowspan="3">R2c3</td></tr>
					<tr><td>R3c0</td><td>R3c1</td><td>R3c2</td></tr>
					<tr><td>R4c0</td><td rowspan="2">R4c1</td><td>R4c2</td><td>R4c3</td></tr>
					<tr><td>R5c0</td><td>R5c1</td><td>R5c2</td><td>R5c3</td></tr>
				</tbody>
			</table>
		</div>
	</div>
	<footer>表格支持表头固定,可视区域宽度或高度不够时会自动出现竖直或水平滚动条。</footer>
	<script>
		/*
		要点:
            1、物理与视觉索引分离:引入 visualCol 循环,而不是直接遍历 tds。通过检查 colNextAvailableRow 数组,判断当前物理位置是否被上方的 rowspan 覆盖。
            2、跳过被占据的格子:如果 colNextAvailableRow[visualCol] 大于当前行索引,说明这个位置是"虚空"的(被上方单元格延伸出来的),直接 continue 到下一列。
            3、处理 colspan 引起的位移:在处理完一个 colspan > 1 的单元格后,通过 visualCol += (colSpan - 1) 让列循环正确跳到下一个真实存在的单元格位置。
            4、准确的颜色交替:colVisualRowIdx 记录的是该列逻辑上垂直方向的"块序列",每次放置一个新块(无论跨几行)后加 1,确保了即便跨行,它与正上方/正下方的块颜色必然不同。
		*/

		function addOddEvenClassesToTable(table) {
			if (!table) return;

			const rows = Array.from(table.querySelectorAll('tr'));
			// 获取总列数
			const headerSource = table.tagName === 'TABLE' ? table : table.closest('table');
			const colCount = headerSource.querySelectorAll('th').length;

			// 关键:记录每一列下一次"可用"的起始物理行索引
			// 以及该列当前的视觉行计数
			const colNextAvailableRow = new Array(colCount).fill(0);
			const colVisualRowIdx = new Array(colCount).fill(1);

			rows.forEach((tr, rowIndex) => {
				const tds = Array.from(tr.querySelectorAll('td'));
				let tdPtr = 0; // 当前处理该行的第几个 td 元素

				for (let visualCol = 0; visualCol < colCount; visualCol++) {
					// 如果当前列在当前行已经被上方的 rowspan 占据,跳过
					if (colNextAvailableRow[visualCol] > rowIndex) {
						continue;
					}

					// 获取当前单元格
					const td = tds[tdPtr];
					if (!td) break;

					const rowSpan = parseInt(td.getAttribute('rowspan')) || 1;
					const colSpan = parseInt(td.getAttribute('colspan')) || 1;

					// 根据该列目前的视觉行索引奇偶性上色
					td.classList.add(colVisualRowIdx[visualCol] % 2 === 1 ? 'odd' : 'even');

					// 更新受该单元格影响的所有列的状态
					for (let i = 0; i < colSpan; i++) {
						const targetCol = visualCol + i;
						if (targetCol < colCount) {
							colNextAvailableRow[targetCol] = rowIndex + rowSpan;
							colVisualRowIdx[targetCol] += 1;
						}
					}

					tdPtr++; // 只有消耗掉一个 td 元素时指针才移动
					// 如果该 td 跨了列,循环会自动增加 visualCol,所以需要手动 offset
					visualCol += (colSpan - 1);
				}
			});
		}
		// 在页面加载完成后执行
		document.addEventListener('DOMContentLoaded', () => {
			const table = document.querySelector('tbody');
			addOddEvenClassesToTable(table);
		});

	</script>
</body>

</html>
相关推荐
ldybk2 小时前
教学vue
前端·javascript·vue.js
英俊潇洒美少年2 小时前
Vue3 实现 AI 流式打字机(SSE+时间切片模拟 React 并发)工程化完整版
前端·人工智能·react.js
开开心心就好2 小时前
操作简单的ISO文件编辑转换工具
java·前端·科技·edge·pdf·安全威胁分析·ddos
盐水冰2 小时前
【HTML】(1)- 演示&标记语言
前端·html
还是大剑师兰特2 小时前
Pinia介绍及Vue3配置示例
前端·javascript·vue.js
李少兄2 小时前
网页应用化部署指南:基于 Edge 浏览器创建桌面快捷方式
前端·edge
IT_陈寒2 小时前
Python的异步陷阱:我竟然被await坑了一整天
前端·人工智能·后端
光影少年2 小时前
Android和iOS原生开发的基础知识对RN开发的重要性,RN打包发布时原生端需要做哪些配置?
android·前端·react native·react.js·ios
Fanfffff7202 小时前
从 6s 到 3s:一次电商前端性能优化实践的系统性总结
前端·性能优化