Соблюдайте мутации на цели node, которая еще не существует

Возможно ли существование мутаций наблюдателя на DOM node, который еще не существует?

Пример:

Мое приложение создает div в какой-то момент: <div id="message" data-message-content="foo" data-message-type="bar" />.

Я хочу следить за созданием и изменением этого div.

var mutationObserver = new MutationObserver(function(mutations){
  // Some code to handle the mutation.
});

mutationObserver.observe(
    document.querySelector('#message'),
        { 
            attributes: true, 
            subtree: true, 
            childList: true, 
            characterData: false 
        }
    );
);

Теперь это возвращает ошибку, поскольку #message имеет значение null (div еще не создан).

Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.

Очевидным решением является просмотр body и проверка того, является ли какая-либо из мутаций созданием div#Message, но это кажется плохой идеей/или, возможно, плохим для производительности.

Ответ 1

Можно наблюдать только существующий node.

Но не беспокойтесь, поскольку getElementById безумно быстро по сравнению с перечислением всех добавленных узлов мутаций, ожидая появления элемента, не будет облагаться налогом вообще, как вы увидите на панели Devtools → Profiler.

function waitForAddedNode(params) {
    new MutationObserver(function(mutations) {
        var el = document.getElementById(params.id);
        if (el) {
            this.disconnect();
            params.done(el);
        }
    }).observe(params.parent || document, {
        subtree: !!params.recursive,
        childList: true,
    });
}

Использование:

waitForAddedNode({
    id: 'message',
    parent: document.querySelector('.container'),
    recursive: false,
    done: function(el) {
        console.log(el);
    }
});

Всегда используйте профилировщик devtools и пытайтесь заставить обратный вызов наблюдателя потреблять менее 1% времени процессора.

  • По возможности наблюдайте прямых родителей будущего node (subtree: false)
  • Используйте getElementById, getElementsByTagName и getElementsByClassName внутри обратного вызова MutationObserver, избегайте querySelector и особенно чрезвычайно медленного querySelectorAll.
  • Если querySelectorAll абсолютно неизбежен в обратном вызове MutationObserver, сначала выполните проверку querySelector, в среднем такая комбо будет намного быстрее.
  • Не используйте методы Array, такие как forEach, filter и т.д., которые требуют обратных вызовов внутри обратного вызова MutationObserver, потому что в Javascript функция invocation является дорогостоящей операцией по сравнению с классическим циклом for (var i=0 ....), а обратный вызов MutationObserver может срабатывать 100 раз в секунду, десятки, сотни или тысячи addedNodes в каждой партии мутаций на сложных современных страницах.
  • Не используйте медленные циклы ES2015, например for (v of something) внутри обратного вызова MutationObserver, если вы не перекомпилируете, а результирующий код работает так же быстро, как классический for.