` 上),并提供可直接运行的结构化代码与关键注意事项。在 HTML 表格中实现“每行显示两个数据项”(例如图书/歌手卡片并排展示),关键在于正确理解表格语义结构:<table> 定义整体容器,<tr> 表示一行,<td> 表示该行中的单元格。初学者常误将 th:each 直接放在 <table> 上(如原代码),导致每个数据项生成一个独立表格,完全破坏布局逻辑。
正确的做法是:在外层 <table> 中遍历数据索引,按步长为 2 动态生成 <tr> 行,并在每行内通过索引计算填充两个 <td> 单元格。以下是优化后的完整代码:
<table class="vocalist-table"> <tr th:each="vocalist, iterStat : ${bookList}" th:if="${iterStat.index % 2 == 0}"> <!-- 左侧单元格:当前索引项 --> <td> <div class="bookdiv" th:id="'vocalist-' + ${iterStat.index}"> <img src="images/ClosedBook.png" class="closeBook" alt="Book cover"> <span class="bookName" th:text="${vocalist.vocaName}">Vocalist Name</span> </div> </td> <!-- 右侧单元格:下一索引项(需校验不越界) --> <td th:if="${iterStat.index + 1 < bookList.size()}"> <div class="bookdiv" th:id="'vocalist-' + ${iterStat.index + 1}"> <img src="images/ClosedBook.png" class="closeBook" alt="Book cover"> <span class="bookName" th:text="${bookList[iterStat.index + 1].vocaName}">Vocalist Name</span> </div> </td> </tr></table>
✅ 核心要点说明:
- th:each 必须作用于 <tr>(而非 <table> 或 <td>),确保每轮迭代生成一行;
- 使用 iterStat.index % 2 == 0 控制仅在偶数索引(0, 2, 4…)时创建新行,自然形成“每行处理两项”的节奏;
- 右侧 <td> 的 th:if 条件防止访问 bookList[size] 导致索引越界(当列表长度为奇数时,最后一行仅渲染左侧一项);
- 为每个 div.bookdiv 添加动态 id(如 vocalist-0),便于后续 JavaScript 交互或样式定位,避免重复 ID(原代码中所有元素 id="startButton" 违反 HTML 规范)。
⚠️ 重要注意事项:
立即学习“前端免费学习笔记(深入)”;
此方案兼顾语义正确性、浏览器兼容性与 Thymeleaf 最佳实践,可稳定支撑中等规模数据的双列表格展示需求。