替代MutationObserver进行同步通知

问题描述 投票:0回答:1

我需要DOM的同步通知更改la MutationEvents才能获得扩展功能。但是,不推荐使用MutationEvent。由于MutationObserver汇总更改并在进行更改后将其交付的方式,因此其用途受到限制。

所以,简单的问题。当前(2019年)浏览器扩展中是否可能同步通知元素样式更改?

javascript google-chrome-extension firefox-addon
1个回答
0
投票

除了您提到的API,没有其他API。唯一的附加方法是钩住Node.prototype.appendChild,还有许多其他方法可以更改page context中的DOM。当然,您还必须钩住innerHTML / outerHTML设置程序之类的东西。

重新定义原型方法可能会破坏某些执行类似低级操作的站点。从理论上讲,至少应予以警告。

这是一个简化的内容脚本,它拦截了一些常用方法:

const eventId = chrome.runtime.id + Math.random().toString(36);
const script = document.createElement('script');
script.textContent = `(${eventId => {
  let reportingEnabled = true;
  // only simple data can be transferred, not DOM elements, not functions, etc.
  const sendReport = detail => dispatchEvent(new CustomEvent(eventId, {detail}));
  const makeHook = (name, fn) =>
    function () {
      if (reportingEnabled) sendReport({name, phase: 'pre'});        
      const res = fn.apply(this, arguments);
      if (reportingEnabled) sendReport({name, phase: 'post'});        
      return res;
    };

  const {appendChild} = Node.prototype;
  Node.prototype.appendChild = 
    Element.prototype.appendChild = makeHook('appendChild', appendChild);

  const {append} = Element.prototype;
  Element.prototype.append = makeHook('append', append);

  const innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
  innerHTML.set = makeHook('innerHTML', innerHTML.set);
  Object.defineProperties(Element.prototype, {innerHTML});
}})('${eventId}')`;

document.documentElement.appendChild(script);
script.remove();

window.addEventListener(eventId, e => {
  console.log(e.detail);
});

显然,您需要钩住所有其他方法,例如removeChild,insertBefore等。

DOM元素无法通过消息传递从页面上下文传递到内容脚本。只有琐碎的类型(如字符串,数字,布尔值,null和由此类类型组成的数组/对象)才可以传输。对于现有的DOM元素,有一个技巧:可以传输其索引[...document.getElementsByTagName('*')].indexOf(element),然后立即将其用作document.getElementsByTagName('*')[index]。对于ShadowDOM,您必须创建一个递归索引器。

© www.soinside.com 2019 - 2024. All rights reserved.