必须在 done 回调里操作 dom,否则找不到元素;layui 表格异步渲染,done 回调确保 tbody 已挂载、数据已写入;合并需基于原始数据比对、清除旧属性、正确设置 rowspan 并隐藏后续单元格。
必须在 done 回调里操作 dom,否则找不到元素
layui 表格是异步渲染的,table.render() 调用后 dom 还没生成,此时查 $('td') 或 $('#demo').find('table') 会返回空集合。唯一可靠时机是 done 回调——它保证 tbody 已挂载、数据已写入、真实表格结构就位。
常见错误包括:
- 把合并逻辑写在
render()调用之后、done之外——几乎必失效 - 用
$('#your-table-id')直接选中,实际匹配到的是外层容器(带layui-table-box的 div),不是真正的数据表格 - 在
init阶段或cols配置里加rowspan: 2——Layui 会忽略该字段
正确做法是限定父容器范围,再找内层表格:
done: function(res, curr, count) {
const $table = $('#container').find('.layui-table-main table'); // ✅ 推荐
// 或
const $table = $('.layui-table-body table'); // ✅ 可用,但多表格时易冲突
const $trs = $table.find('tbody tr');
}
合并哪一列?先确认真实 colIndex,别硬编码
Layui 渲染后,<td> 的顺序不等于 <code>cols 数组索引。比如你配置了 {field: 'name', title: '姓名'},但它前面有复选框列、序号列、固定列,那它在 DOM 中可能位于第 3 个 <td>(索引 <code>2),而非 0。
安全获取方式:
- 用浏览器开发者工具 inspect 目标
<td>,看它是 <code><tr> 下第几个子节点(从 0 开始)<li>用 <code>$('th[data-field="name"]').index()查表头列位置(th和td索引一致) - 如果该列设了
fixed: 'left',它会被渲染到独立的<table> 中,不能和主表混算索引<p>注意:合并逻辑必须基于原始数据比对,而不是 <code>td.textContent——后者含图标、HTML 标签时会误判。应和res.data[i][field]对齐。手动合并三步:清旧、比对、设
rowspan+display: none核心不是“视觉连在一起”,而是按数据连续性计算跨行数,并显式设置
rowspan、隐藏后续单元格。漏掉任一环节都会出错:- 不先清除旧的
rowspan和display: none,翻页或重载后会叠加失效 - 遍历时没跳过已设
rowspan > 1的<td>,会导致重复合并、错位<li>用 <code>visibility: hidden替代display: none——打印/导出时仍占位,表格错乱
示例(合并第 2 列,字段为
'category'):function mergeByField($trs, data, field, colIndex) { let mark = 1; let mergeStart = 0; for (let i = 1; i 1) { $trs.eq(mergeStart).find('td').eq(colIndex).attr('rowspan', mark); for (let j = 1; j <h3>排序、分页、搜索后必须重执行,不能只 run 一次</h3><p>Layui 所有交互操作(点击表头排序、切换分页、触发搜索)都会销毁并重建 <code>tbody</code>,之前写的 <code>rowspan</code> 和 <code>display: none</code> 全部丢失。这意味着合并逻辑不是“初始化一次就行”,而是每次 <code>done</code> 触发都得完整跑一遍。</p><p>容易被忽略的点:</p>- 如果你把合并函数抽成独立方法但没在每次
done里调用,只有首次加载生效 - 用
table.reload()刷新数据时,done仍会触发,合并逻辑必须包含在内 - 合并涉及多列时(如先按 A 列合并,再按 B 列合并),要确保顺序合理,避免第二轮覆盖第一轮的
rowspan
真正麻烦的不是算法本身,而是时机、索引、状态清理这三件事必须同步到位——少一个,表格就裂开。
- 不先清除旧的











