基于contentEditable实现类Word修订功能时如何对内容内联节点分组
内联修订节点分组实现方案
核心思路
给所有修订节点新增标准化自定义属性,通过匹配属性+节点位置关系完成逻辑分组,从源头上也可以优化节点生成规则减少零散节点产出。
步骤1:给修订节点增加统一标识
你之前生成插入、删除节点时,不要仅添加样式属性,必须同步附加自定义数据属性,示例结构如下:
- 插入内容节点:
<u data-change-type="insert" data-change-id="唯一随机值" data-change-session="本次修订会话ID" data-change-author="操作人ID">新增的文本</u> - 删除内容节点:
<del data-change-type="delete" data-change-id="唯一随机值" data-change-session="本次修订会话ID" data-change-author="操作人ID">被删除的文本</del>data-change-session可在开启修订追踪时生成,用时间戳或随机字符串均可,用来区分不同时间、不同用户提交的修改
步骤2:两种分组逻辑实现
按连续同属性节点分组(适合合并用户连续操作生成的零散节点)
触发批量操作前,遍历目标段落的所有子节点,按照规则自动合并连续的同属性修订节点,示例逻辑代码如下:
function groupContinuousChanges(paragraphElement) { const changeGroups = [] let currentGroup = null // 遍历段落的所有子节点 Array.from(paragraphElement.childNodes).forEach(node => { // 遇到非修订节点直接中断当前分组 if (!node.dataset || !node.dataset.changeType) { currentGroup = null return } // 生成当前修订的唯一特征key const currentKey = `${node.dataset.changeType}-${node.dataset.changeSession}-${node.dataset.changeAuthor}` // 无当前分组或特征不匹配时新建分组 if (!currentGroup || currentGroup.groupKey !== currentKey) { currentGroup = { groupKey: currentKey, type: node.dataset.changeType, nodes: [] } changeGroups.push(currentGroup) } currentGroup.nodes.push(node) }) return changeGroups }
拿到分组后直接按组执行操作即可:
- 接受插入组:移除节点的
u标签和自定义属性,保留内部文本 - 接受删除组:直接移除所有
del节点 - 拒绝插入组:直接移除所有
u节点 - 拒绝删除组:移除节点的
del标签和自定义属性,保留内部文本
按段落全量修订分组(适合一键处理整段所有修改)
如果需要整段修改一键接受/拒绝,不需要做连续节点合并,直接筛选出段落内所有带data-change-type属性的节点,全部归为同一个段落修订组即可,处理逻辑和单组操作完全一致。
优化方案:从源头减少零散节点
你现在产生大量零散内联节点的核心原因是每次输入单个字符就新建一个修订节点,可以优化输入拦截逻辑:输入时先判断光标前一个节点是否为同类型的修订节点,如果是就直接把新输入的字符插入到该节点内部,不需要新建节点,从生成阶段就降低后续分组的压力。
内容的提问来源于stack exchange,提问作者Sergei Masliukov
相关产品推荐
相关产品推荐

