能监控,但必须聚焦“安全属性”本身——只捕获class、style、disabled、data-*等直接影响行为或可见性的属性,并在回调中做语义级判定;因attributeFilter不支持通配符且硬编码易失效,需启用attributes: true + attributeOldValue: true手动比对变更。
直接结论:能监控,但必须聚焦“安全属性”本身——不是监听所有属性变更,而是只捕获 class、style、disabled、data-* 等直接影响行为或可见性的属性,并在回调中做语义级判定,否则等于白开。
attributes: true + attributeFilter
因为 attributeFilter: ['class', 'style'] 在实际中几乎无效:浏览器对通配符(如 'data-*')不支持,传进去会被静默忽略;而硬写全量 data-original-amount、data-auth-required 等字段又极易失效——第三方插件常动态生成 data 属性名。真正可靠的做法是开启 attributes: true + attributeOldValue: true,然后在回调里手动比对变更前后的值,只响应那些导致功能降级或视觉隐藏的操作。
不是所有属性修改都危险,关键看是否破坏用户可控性或信息完整性:
class 被移除 is-enabled 或添加 hidden-by-sdk 类 → 按钮不可点style 新增 display: none 或 opacity: 0 → 关键文案/按钮消失disabled 从 false 变为 true → 表单控件被锁死data-* 属性被注入伪造值(如 data-verified="false" 覆盖真实状态)别遍历所有 mutation,先做三层轻量过滤:
!target.closest('#pay-section, #login-form, [data-role="auth"]')
mutation.type === 'attributes' 且 mutation.attributeName 在白名单中(['class', 'style', 'disabled', 'readonly', 'data-status'])if (oldValue === null && newValue !== null && newValue.includes('none')) → 高概率是隐藏操作一旦确认篡改,修复动作必须原子化且隔离:
observer.disconnect() 再修改 DOM,修完再 observer.observe() —— 否则 el.classList.remove('hidden-by-sdk') 会再次进回调,可能死循环innerHTML 重写整个容器,优先用 el.setAttribute('class', originalClass) 或 el.disabled = false
if (Date.now() - lastFixTime
真正难的不是监听,而是定义“什么是安全”——比如 data-tracked="true 是埋点需要,data-tracked="blocked" 才是干预信号。这个边界必须由业务方明确,MutationObserver 只负责忠实传递变更事实。