done回调里直接操作DOM会失效,因Layui渲染后仍会执行scrollbar补丁注入和固定列计算,导致tbody被重排或替换;应使用templet列配置或setTimeout(0)延后操作。
done 回调里直接操作 DOM 会失效?
因为 done 是 layui 表格渲染完成后的回调,但此时表格主体(tbody)可能已被 laytable 内部用虚拟 dom 或重绘逻辑覆盖——你手动改的 dom 很可能在下一次排序、分页或搜索时被清掉。
常见错误现象:done 里用 document.querySelector 找到单元格并 innerHTML = 'xxx',页面一闪而过就恢复原样;或者点击分页后修改消失。
- 必须等 Layui 完成最终渲染且不再干预 DOM 时再操作,推荐用
setTimeout(..., 0)微任务延后 - 优先用
templet列配置做静态渲染,done只处理动态/条件性 DOM(比如根据接口状态加 badge) - 避免直接改
tr或td的innerHTML,改innerText或加 class 更安全
修改某列内容:用 templet 比 done 更稳
如果只是想把「状态码 1 → 显示“启用”,0 → “停用”」,别在 done 里遍历 DOM 改,Layui 原生支持模板列。
示例(JavaScript 初始化表格时):
cols: [[
{field: 'status', title: '状态', templet: function(d) {
return d.status === 1
? '<span class="layui-badge layui-bg-green">启用</span>'
: '<span class="layui-badge layui-bg-gray">停用</span>';
}}
]]
-
templet在每次重绘(包括排序、搜索、分页)都会重新执行,天然同步 - 不用管
done,也不用监听事件,代码更少、不易出错 - 若需异步数据(比如根据
d.id请求用户角色),templet不适用,才退回到done+setTimeout
done 中修改 DOM 的正确姿势
真要动态加按钮、绑定 click、插入远程加载的内容,得确保操作发生在 Layui 渲染闭环之后。
示例(给每行加一个「编辑」按钮,并绑定事件):
done: function(res, curr, count) {
// 等 layTable 完全释放 DOM 控制权
setTimeout(() => {
const trs = document.querySelectorAll('.layui-table-body tbody tr');
trs.forEach((tr, i) => {
const data = res.data[i]; // 注意:res.data 是当前页数据
const btn = tr.querySelector('.edit-btn');
if (btn && data) {
btn.onclick = () => editUser(data.id);
}
});
}, 0);
}
- 必须用
res.data[i]对应当前页第 i 行数据,不能依赖tr.dataset.index(Layui 不写这个) - 不要用
$(...).on('click', ...),Layui 未引入 jQuery 时会报错;纯 JS 绑定更可靠 - 如果表格启用了
skin: 'line'或size: 'sm',DOM 结构不变,不影响选择器
为什么不能在 render 完立刻改?
Layui 表格的 done 触发时机是「数据已载入、DOM 已生成」,但内部还有两步没完:scrollbar 补丁注入 和 固定列位置计算。这两步会触发 tbody 重排或替换,你提前改的 DOM 就丢了。
- Chrome DevTools 里打断点看
done执行时,tbody的childElementCount可能还是 0,稍后才填入tr -
setTimeout(..., 0)不是“随便加”,而是让 JS 引擎把控制权交还给浏览器,等它完成样式计算和布局后再执行 - 若涉及大量 DOM 操作(如每行加 3 个按钮),建议用
DocumentFragment批量插入,避免重排抖动
最麻烦的其实是分页切换时的 DOM 复用——Layui 会复用 tr 元素并只更新 innerText,所以直接改 innerHTML 后,复用时旧结构还在,新数据没刷进去。这事容易被忽略,直到用户狂点下一页才暴露。










