不能直接用 Set 存储 DOM 节点引用管理选中状态,因节点可能被移除或重渲染导致悬空引用;应改用唯一 ID(如 data-node-id)存入 Set,配合查询与清理机制保障健壮性。

在前端富文本编辑器中,Set 本身**不能直接安全地存储 DOM 节点引用**用于长期选中状态管理,原因在于节点可能被移除、替换或重渲染(如使用 React/Vue 等框架),导致 Set 中的引用变成“悬空引用”(dangling reference),进而引发内存泄漏或运行时错误。
为什么直接存 DOM 节点不推荐
富文本编辑器中节点频繁变动:内容修改、撤销重做、格式切换、协作编辑、框架 diff 更新等,都可能导致原始节点被销毁。此时 Set.has(node) 仍返回 true,但该节点已不在文档中,甚至无法调用 node.parentElement 等属性 —— 这不是 Set 的问题,而是 DOM 生命周期与引用管理的错配。
更可靠的做法:用唯一标识代替节点引用
给每个可选中的节点(如 <span></span>、<strong></strong>、自定义 inline element)分配一个稳定、可序列化的 ID,并用 Set<string></string> 存储这些 ID:
- 初始化时,为新插入的可选中节点添加
data-node-id="xxx"属性(例如用crypto.randomUUID()或递增计数器生成) - 用户点击/框选时,收集目标节点的
node.dataset.nodeId,加入selectedIds = new Set() - 需要高亮或操作选中项时,用
document.querySelectorAll('[data-node-id]')批量查找,再过滤出仍在 DOM 中且 ID 匹配的节点 - 每次编辑后(如粘贴、格式化),可清理不再存在的 ID:
for (const id of selectedIds) { if (!document.querySelector(`[data-node-id="${id}"]`)) selectedIds.delete(id); }
配合 Range/Selection 实现精准选中同步
仅靠 ID 集合不足以还原光标位置或文本范围。实际场景中建议分层管理:
立即学习“Java免费学习笔记(深入)”;
-
视觉高亮层:用
Set<string></string>管理「当前应高亮的节点 ID」,通过 CSS 类(如.selected)动态控制样式 -
逻辑选中层:监听
selectionchange,用window.getSelection()获取真实Range,解析出覆盖的可选中节点 ID,更新Set - 持久化层:若需跨会话保留(如草稿保存),只序列化 ID 集合 + 光标锚点偏移(而非 DOM 引用)
框架场景下的注意事项
在 React/Vue/Svelte 中,节点由虚拟 DOM 控制,手动挂载 data-node-id 后还需确保:
- ID 在组件 key 或 state 中保持稳定(避免重渲染时 ID 变更)
- 卸载组件前,从
Set中清除对应 ID(或使用 WeakMap 关联组件实例生命周期) - 避免在
useEffect或onMounted中直接遍历并添加所有节点到 Set —— 应响应式监听 Selection 变化,按需同步
不复杂但容易忽略:DOM 引用是易逝的,ID 是契约;用 Set 管理 ID 是轻量且健壮的选择,关键在建立和维护这个契约。


















