javascript - 绕过现有的 MutationObserver 以使用 Tampermonkey 插入 DOM 元素

标签 javascript tampermonkey mutation-observers

我编写了一个 Tampermonkey 脚本,将一些 DOM 元素注入(inject)到我用于工作的网站中,以使导航更容易。它不再有效,因为 vendor 引入了一个 MutationObserver,大概是为了阻止像我这样的人这样做。为什么,我不知道。我没有做任何邪恶的事情。

我知道这是怎么回事,因为我可以看到我的 DOM 元素短暂出现然后消失。它们不再位于 DOM 树中,站点的 JS 包含 MutationObservers 的代码:

// this is a property of a large object
value: function() {
  var e = this
  if (this._options.shouldContainFocus) {
    this._observer = new MutationObserver(this.handleDOMMutation)
    for (var t = this._contextElement; t && 1 === t.nodeType && "BODY" !== t.tagName;) {
      var n = t.parentElement
      if (n) {
        this._parents.push(n)
        this.muteNode(n)
        Array.prototype.slice.call(n.childNodes).forEach(function(t) {
          e.hideNode(t)
        })
      }
      t = t.parentNode
    }
  }
}

// this is what is called by the MutationObserver
this.handleDOMMutation = function(e) {
  e.forEach(function(e) {
    Array.from(e.addedNodes).forEach(function(e) {
      n.hideNode(e)
    })
    e.removedNodes.forEach(function(e) {
      var t = n._nodes.indexOf(e)
      t >= 0 && n._nodes.splice(t, 1)
    })
  })
}

所以我的问题是...我怎样才能解决这个问题(如果有的话)?我所能想到的就是访问一些全局的 MutationObservers 列表,但从我所读到的内容来看,这似乎不存在(直到今天我才听说过这个 API)。观察者变量不是我可以通过 unsafeWindow 访问的变量,因此我无法调用 disconnect 方法。我已经阅读了有关 MutationObservers 的文档,但找不到任何可以帮助我的东西。有什么建议吗?

最佳答案

如评论中所述,这是一种在文档加载之前覆盖 window.MutationObserver 的可能方法。因为 MutationObserver 被作为构造函数调用,为了网站的脚本不抛出错误,请确保让你的 monkeypatched MutationObserver 可以作为构造函数调用。然后,由于实例化的对象可以调用它的方法(如 observedisconnect),你可能会返回一个 Proxy ,当一个属性被访问(如 observer),返回一个可调用的函数,但不执行任何操作。例如:

const div = document.querySelector('div');
window.MutationObserver = function() {
  return new Proxy({}, { get: () => () => null })
};

new MutationObserver(() => {
  console.log('saw a mutation');
}).observe(div, { childList: true });
div.appendChild(document.createElement('span')).textContent = 'bar';
<div>foo</div>

如果 window.MutationObserver 的初始重新分配发生在站点脚本运行之前,则调用 new MutationObserver 将返回不执行任何操作的观察器。 (正如您在上面的代码中看到的,如果您删除 window.MutationObserver 的重新分配,您将看到记录了 'saw a mutation',而在 monkeypatching 之后,什么也没有已记录。)

因此,将其转换为用户脚本,粘贴到 window.MutationObserver 重新分配,并确保用户脚本在 document-start 运行,以便脚本运行在站点的内置脚本运行之前。如果您还希望在自己的代码中使用 MutationObserver,请在重新分配之前保存对 window.MutationObserver 的引用:

// ==UserScript==
// @name         New Userscript
// @match        https://somewhere
// @run-at       document-start
// ==/UserScript==

const oldMutationObserver = window.MutationObserver;
window.MutationObserver = function() {
  return new Proxy({}, { get: () => () => null })
};

如果您所在的站点依赖于 MutationObserver 属性和函数来返回合理的值(如 takeRecords()),那么与其返回 Proxy 什么都不做,你可能会返回一个代理,它可以访问 MutationObserver 的实际实例化,当 observe 被调用时,它会观察 不同的元素(一个永远不会改变的元素):

// Userscript code:

const oldMutationObserver = window.MutationObserver;
const elementThatIsNeverMutated = document.createElement('div');
window.MutationObserver = function(callback) { // callback should never be called
  const observer = new oldMutationObserver(callback);
  return new Proxy(observer, { get: (obj, prop) => {
    if (prop === 'observe') return (targetNode, config) => {
      obj.observe.call(obj, elementThatIsNeverMutated, config);
    };
    const val = obj[prop];
    return typeof val === 'function' ? val.bind(obj) : val;
  }})
};

// Example of site's built-in code:

const div = document.querySelector('div');
const observer = new MutationObserver(() => {
  console.log('saw a mutation');
});
observer.observe(div, { childList: true });
div.appendChild(document.createElement('span')).textContent = 'bar';
const arr = observer.takeRecords();
arr.forEach(() => console.log('some item in array'));
<div>foo</div>

关于javascript - 绕过现有的 MutationObserver 以使用 Tampermonkey 插入 DOM 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52359715/

相关文章:

javascript - Angular UI-Router 中的嵌套 View 在状态更改期间滞后

javascript - Tampermonkey 未显示 Chrome 通知

javascript - 如何通过 TamperMonkey 设置文本字段的值?

security - 检测、更改或删除现有的变异观察者

javascript - 观察可构造样式表的变化

mutation-observers - 如何将mutationobserver注入(inject)puppeteer

javascript - 双下拉列表框页面重定向php wordpress

javascript - 如何检查坐标是否在以公里为单位的半径范围内?

javascript - Chrome 10 : Custom Video Interface Problem

javascript - 如何将另一个网页中的表格加载到数组中?