替代MutationObserver进行同步通知 [英] Alternative to MutationObserver for synchronous notifications

查看:220
本文介绍了替代MutationObserver进行同步通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need synchronous notifications of DOM changes a la MutationEvents for an extension capability. MutationEvents, however, are deprecated. MutationObserver is limited in usefulness because of the way it aggregates changes and delivers them after the changes have been made.

那么,一个简单的问题.当前(2019)浏览器扩展中是否可以同步通知元素样式更改?

So, simple question. Is synchronous notification of Element style changes possible in current (2019) browser extensions?

推荐答案

除了您提到的API外,没有其他API.唯一的其他方法是钩住Node.prototype.appendChild,还有许多其他方法可以更改页面上下文中的DOM.自然,您还必须钩住innerHTML/outerHTML设置器之类的东西.

There's no API other than those you've mentioned. The only additional approach is to hook Node.prototype.appendChild, and a bunch of other methods to alter DOM in page context. Naturally you'll have to hook things like innerHTML/outerHTML setters as well.

重新定义原型方法可能会破坏某些做类似底层操作的网站.
从理论上讲,至少应予以警告.

Redefining prototype methods may break some sites that do similar low-level things.
Theoretically, at least, so be warned.

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

Here's a simplified content script that intercepts a few common methods:

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等.

Obviously you'll need to hook all the other methods like removeChild, insertBefore, and so on.

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

DOM elements cannot be transferred via messaging from the page context to the content script. Only trivial types like strings, numbers, boolean, null, and arrays/objects that consist of such types are transferable. There's a trick though for an existing DOM element: you can transfer its index [...document.getElementsByTagName('*')].indexOf(element) and then use it immediately as document.getElementsByTagName('*')[index]. For ShadowDOM you'll have to make a recursive indexer.

这篇关于替代MutationObserver进行同步通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆