如何通过MutationObserver阻止或回退DOM节点的变更操作?
MutationObserver 批量回退DOM变更实现方案
首先明确核心逻辑:MutationObserver 是DOM变更完成后才触发回调的API,没有原生拦截变更的能力,你需要根据本次批次返回的mutationsList记录,手动逆向还原所有变更。
完整实现代码
const targetElement = document.body; const config = { attributes: true, childList: true, subtree: true, // 必须开启旧值记录,否则属性变更无法回退 attributeOldValue: true, characterData: true, characterDataOldValue: true }; const revertMutations = (mutationsList) => { // 倒序回退,避免先后变更的依赖导致回退失效 for (let i = mutationsList.length - 1; i >= 0; i--) { const mutation = mutationsList[i]; switch (mutation.type) { // 处理节点增删变更 case 'childList': // 回退新增的节点:直接移除 mutation.addedNodes.forEach(node => node.remove()); // 回退移除的节点:插回原位置 mutation.removedNodes.forEach(node => { mutation.target.insertBefore(node, mutation.nextSibling); }); break; // 处理属性变更 case 'attributes': mutation.target.setAttribute( mutation.attributeName, mutation.oldValue ); break; // 处理文本内容变更 case 'characterData': mutation.target.data = mutation.oldValue; break; } } } const callback = (mutationsList, observer) => { const isExampleMutation = mutationsList.find(mutation => mutation.target.id === "example-id"); if (isExampleMutation) { // 先暂停观察,避免回退操作本身触发新的回调造成死循环 observer.disconnect(); // 回退本次批次的所有变更 revertMutations(mutationsList); // 重新启动观察 observer.observe(targetElement, config); } } const observer = new MutationObserver(callback); observer.observe(targetElement, config);
注意事项
- 倒序回退是必要操作:如果同一批次内存在先后关联的变更,正序回退会出现节点找不到、位置错误等问题
- 如果
#example-id的变更非常频繁,建议单独针对该节点做观察和回退,不需要每次回退整个批次的所有变更,性能会更好 - 如果回退后页面脚本仍然会反复修改该节点,可以给节点加自定义标记位,在回调中判断如果是你自己的回退操作触发的变更就直接跳过,避免无意义的循环回退
内容的提问来源于stack exchange,提问作者goo6
相关产品推荐
相关产品推荐

