layui tree 动态操作本质是修改 treedata 后调用 tree.render(),无原生增删改 api;2.8+ 的 updatenode 仅更新已有节点字段,不处理结构变更或懒加载子树;id 必须全局唯一稳定,勾选状态需同步维护。

layui tree 没有原生的 addNodes、deleteNode 或 updateNode 方法——所有“动态操作”本质都是改数据再重绘,不操作 DOM,不调 API 就没用。
手动增删节点必须改 treeData 数组再调 tree.render()
layui tree 渲染后,DOM 和数据完全脱钩。你用 jQuery 删除某个 .layui-tree-node,下次 tree.render() 一执行,立刻恢复原样。
- 新增节点:确认好
id(唯一)、title(必填)、pid(父节点 id),找到对应父节点的children数组,push进去;根节点就直接往treeData顶层数组push - 删除节点:不能只删当前层,必须递归过滤掉该节点及其全部后代;用
id做标识,写个递归函数遍历并filter - 修改节点:直接在
treeData中定位到该节点(可用find或递归查找),改title、href等字段即可 - 改完必须调
tree.render({ elem: '#tree', data: treeData }),否则界面毫无变化
tree.updateNode() 只在 layui 2.8+ 有,且只更新已渲染节点
旧版本(如 2.7.x)根本不存在这个方法,别查文档白费劲;2.8+ 虽有,但它只作用于当前 data 数组中已存在的节点,不会自动插入新节点或处理懒加载未展开的子树。
- 调用前确保节点已在
treeData中(不是 DOM 里有就行) -
id类型要和原始数据一致(比如原始是字符串"123",就不能传数字123) - 它不改变节点位置(比如移到另一个父节点下),只改字段值;移位仍得手动挪
children数组 - 如果只是改文本,
tree.updateNode(id, { title: '新名字' })最简;但涉及结构变动,还是老老实实改数组 +render
开启 showCheckbox: true 后,增删节点必须同步勾选状态
layui 内部维护了一套独立的勾选记录,和你的 treeData 是两套体系。删了一个已勾选节点,但没清理勾选 ID 列表,重绘后可能出现“半选图标残留”或“父节点勾选状态错乱”。
- 操作前先存状态:
const checkedIds = tree.checkStatus('demoId').checked.map(x => x.id) - 删完节点后,对
checkedIds做filter,剔除那些在新treeData中已不存在的id - 重绘后立即调
tree.setChecked('demoId', { checked: filteredIds })(注意:2.8+ 才支持此写法) - 懒加载节点未展开时,其子节点不会出现在
checkStatus()结果里——这点极易漏判,建议操作前强制展开关键分支或改用全量数据管理
双击编辑、自定义按钮等交互必须自己绑定事件,别依赖 edit: ['add']
edit: ['add', 'del'] 渲染出的默认图标功能极弱,且无法控制逻辑(比如加校验、弹窗、请求后端)。真正可控的做法是关掉它,自己写按钮 + 事件监听。
- 禁用默认操作:
edit: false,然后在节点click回调里判断右键/点击次数/自定义属性做分流 - 双击改名:监听
.layui-tree-txt的dblclick,用e.stopPropagation()阻止冒泡,动态插入input,Enter 失焦后调tree.updateNode()或直接改treeData - 自定义按钮:在
text字段里拼 HTML,或用done回调遍历 DOM 插入按钮,再绑定事件;注意事件委托,避免动态节点失效 - 所有这类操作,最终落点仍是改
treeData+tree.render()—— 这条链路绕不开
最易被忽略的一点:树节点的 id 必须全局唯一且稳定。前后端交互时若用数据库自增 ID 做 id,前端新增临时节点就得用负数或字符串前缀(如 "new_1"),否则保存后 ID 冲突或覆盖。这个 ID 不仅用于渲染,还绑着 checkbox 状态、事件定位、父子关系,动它就要通盘考虑。











