基于Safari WebExtension API的DOM替换撤销功能实现咨询
实现思路与方案
这里给出两种不需要反向编写替换逻辑、也不用刷新页面的实现方案,均兼容Safari WebExtension API规范:
方案1:文本节点快照存储(适用于仅修改文本内容的场景)
这是最轻量的实现方式,全程不需要复用你的替换逻辑:
- 执行替换操作前,先遍历所有你即将修改的文本节点,将节点引用、原始文本内容存储到
content.js的全局数组中,注意跳过script、style等非内容节点,以及扩展自身注入的DOM元素
参考代码片段:// 全局存储替换快照,每次执行新替换前清空即可 let replaceSnapshots = []; // 执行替换前先遍历收集快照 function collectSnapshot(node) { if (node.nodeType === Node.TEXT_NODE) { const parentTag = node.parentElement.tagName; if (parentTag === 'SCRIPT' || parentTag === 'STYLE' || parentTag.classList.contains('your-ext-class')) return; // 仅收集会被你替换逻辑命中的节点,避免冗余存储 if (node.textContent.match(你的替换匹配正则)) { replaceSnapshots.push({ node: node, rawText: node.textContent }); } } node.childNodes.forEach(child => collectSnapshot(child)); } - 正常执行你的替换逻辑修改文本内容即可
- 撤销操作直接遍历快照数组还原:
function undoReplace() { replaceSnapshots.forEach(item => { // 兼容节点已被页面原生逻辑删除的场景,避免报错 if (item.node.isConnected) { item.node.textContent = item.rawText; } }); // 清空快照避免重复撤销 replaceSnapshots = []; }
方案2:DOM片段快照存储(适用于替换时会新增DOM元素的场景)
如果你替换单词的时候会给目标单词加样式标签、点击事件等修改DOM结构的操作,可以直接存对应区域的原始DOM快照:
- 替换前将整个
body或者你修改的目标区域的innerHTML存到全局变量/browser.storage.local中 - 撤销的时候直接将存储的原始HTML覆写到对应区域即可,性能开销也远低于刷新页面
额外注意事项
Safari的content script上下文在页面刷新、跨域导航后会自动销毁,不需要额外处理快照的内存释放。如果需要支持关闭popover后下次打开还能撤销,可以把快照序列化为JSON存在browser.storage.local中,还原时重新匹配对应节点即可。
内容的提问来源于stack exchange,提问作者srb7
相关产品推荐
相关产品推荐

