Измените элементы сразу после их отображения (не после полной загрузки страницы) в greasemonkey script?

У меня этот script. Он применяется на страницах фильмов RottenTomatoes и изменяет 3 элемента
(1 видимый текст и 2 текста внутри всплывающих подсказок).

В настоящее время (из-за greasemonkey @run-at document-end) он изменяет элементы только после полной загрузки страницы.

Кроме того, существует много раз, когда страницы RottenTomates задерживают загрузку, до 20 секунд (!),
так что дополнительная задержка для моего script.

Это вызывает две вещи:

  • задержка в отображении измененного видимого текста и
  • если вы наведите указатель мыши на любую из всплывающих подсказок до того, как страница будет полностью загружена, то после ее загрузки она, вероятно, будет содержать только исходный текст без моего добавления или просто мое добавление.

Чтобы увидеть это, установите script, а затем зайдите в this типичная целевая страница.


Страницы RottenTomatoes загружают различный контент через XHR (как я видел в Firefox Netowork Monitor),
но 3 элемента (которые я хочу изменить) отображаются немедленно, а не когда страница полностью загружена.


Я попытался использовать MutationObserver, чтобы посмотреть, какое конкретное текстовое значение появится на экране, но оно не работает. Его структура такова:

var target = selector_for_element_containing_text ;    // the selector is:  document.querySelector('.audience-info > div:nth-child(1)');
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
       modifyElements();
   });
});
var config = { attributes: true, childList: true, characterData: true, subtree: true };
observer.observe(target, config);


function modifyElements(){  // This is just the current code of my script
    // modify element 1 
    // modify element 2         
    // modify element 3 
}

Как изменить элементы сразу после их отображения?

Ответ 1

  • Сделайте script запустите document-start, добавив этот код в script metablock:

    ..............
    // @run-at        document-start
    ..............
    // ==/UserScript==
    
  • Теперь, когда script ничего не запускает в документе, поэтому мы присоединяем наблюдателя к корню документа и сканируем добавленные узлы для элемента контейнера .audience-info:

    var observer = new MutationObserver(function(mutations) {
        for (var i=0; i<mutations.length; i++) {
            var mutationAddedNodes = mutations[i].addedNodes;
            for (var j=0; j<mutationAddedNodes.length; j++) {
                var node = mutationAddedNodes[j];
                if (node.classList && node.classList.contains("audience-info")) {
                    node.firstElementChild.innerHTML = node.firstElementChild.innerHTML
                        .replace(/[\d.]+/g, function(m) { return 2 * m });
                    // don't hog resources any more as we've just done what we wanted
                    observer.disconnect();
                    return;
                }
            }
        }
    });
    observer.observe(document, {childList: true, subtree: true});
    

    При вызове наблюдателя всплывающие подсказки уже присутствуют в дереве документов, поэтому вы можете просто переместить код из функции "load" в наблюдателя до return, не изменяя ничего.

N.B. Лучше всего сделать наблюдателя как можно быстрее, в частности, используя простые for -loops вместо обратных вызовов функций из-за накладных расходов, чтобы вызвать их, что может стать проблемой на медленном ПК/устройстве при открытии очень сложной страницы, генерирует тысячи (довольно общий случай) или сотни тысяч мутаций (крайне редко, но все же!). И отключите его, как только работа будет выполнена.

P.S. С помощью setMutationHandler код наблюдателя будет выглядеть следующим образом:

// @require       https://greasyfork.org/scripts/12228/code/setMutationHandler.js
// ==/UserScript==

setMutationHandler(document, '.audience-info div:first-child', function(nodes) {
    this.disconnect();
    nodes.forEach(function(n) {
        n.innerHTML = n.innerHTML.replace(/[\d.]+/g, function(m) { return 2*m });
    });
});