拦截和修改 innerHTML 的 getter/setter
要实现这个功能,我们需要使用 JavaScript 的 Object.defineProperty
来拦截元素的 innerHTML
属性。以下是完整的解决方案:
实现代码
// 存储原始值和对应的随机字符串标记
const originalHtmlMap = new WeakMap();
function interceptInnerHTML(element) {
// 保存原始的 innerHTML getter/setter
const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
Object.defineProperty(element, 'innerHTML', {
get: function() {
// 获取当前值
const currentValue = descriptor.get.call(this);
// 如果有存储的原始值,就还原它
if (originalHtmlMap.has(this)) {
const { originalHtml, markedHtml } = originalHtmlMap.get(this);
if (currentValue === markedHtml) {
return originalHtml;
}
}
return currentValue;
},
set: function(value) {
// 生成随机字符串标记
const randomStr = Math.random().toString(36).substring(2, 15);
// 在原始 HTML 中添加标记(这里简单地在开头添加)
const markedValue = `<!-- ${randomStr} -->${value}`;
// 存储原始值和标记后的值
originalHtmlMap.set(this, {
originalHtml: value,
markedHtml: markedValue
});
// 设置标记后的值
return descriptor.set.call(this, markedValue);
}
});
}
// 使用示例:拦截某个元素的 innerHTML
const targetElement = document.getElementById('myElement');
interceptInnerHTML(targetElement);
// 测试设置和获取
targetElement.innerHTML = '<div>Hello World</div>';
console.log(targetElement.innerHTML); // '<div>Hello World</div>'(自动去除了标记)
console.log(targetElement.innerHTML); // '<div>Hello World</div>'(看起来是原始值)
关键点说明
WeakMap存储:使用 WeakMap 来存储元素与其原始 HTML/标记 HTML,避免内存泄漏。
getter逻辑:
- 检查当前元素是否有存储的原始值
- 如果当前值与存储的标记值匹配,则返回原始值
setter逻辑:
- 为每个设置的 HTML 添加唯一随机字符串标记(这里简单使用了注释)
- 同时保存原始值和标记后的值
多次调用处理:
- WeakMap会跟踪每个元素的最后一次设置的值
- getter总是返回最近一次设置的原始值
注意事项
性能影响:这种拦截会影响 DOM操作性能,特别是频繁操作时。
兼容性:确保目标浏览器支持 WeakMap。
更复杂的场景:如果需要更精确地追踪多次修改,可能需要维护一个修改历史栈而非只保存最新值。
DOM变化检测:某些框架可能会直接操作 DOM而不通过 innerHTML,这种情况下不会被拦截到。
内容由零声教学AI助手提供,问题来源于学员提问