Альтернатива MutationObserver для синхронных уведомлений - PullRequest
0 голосов
/ 31 октября 2019

Мне нужны синхронные уведомления об изменениях DOM а-ля MutationEvents для возможности расширения. Однако MutationEvents устарели. MutationObserver ограничен в полезности из-за того, как он агрегирует изменения и доставляет их после внесения изменений.

Итак, простой вопрос. Возможно ли синхронное уведомление об изменениях стиля элемента в текущих (2019) расширениях браузера?

1 Ответ

0 голосов
/ 31 октября 2019

Нет API, кроме тех, которые вы упомянули. Единственный дополнительный подход заключается в подключении Node.prototype.appendChild и нескольких других методов для изменения 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 и т. д. on.

Элементы DOM не могут быть переданы посредством обмена сообщениями из контекста страницы в сценарий содержимого. Переносимы только тривиальные типы, такие как строки, числа, логические, нулевые и массивы / объекты, которые состоят из таких типов. Однако для существующего элемента DOM есть хитрость: вы можете передать его индекс [...document.getElementsByTagName('*')].indexOf(element), а затем сразу использовать его как document.getElementsByTagName('*')[index]. Для ShadowDOM вам придется создать рекурсивный индексатор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...