Почему postMessage () дает мне информацию только из последнего DOM? - PullRequest
0 голосов
/ 28 марта 2020

Браузер: Google Chrome (Win 10x64)

Впервые я использую postMessage API javascript, поэтому мне не известны все его нюансы.

Я пытаюсь l oop над набором элементов DOM, а затем открываю кучу ссылок на разных вкладках. Когда эти вкладки открыты, найдите определенные элементы в соответствующих DOM и отобразите их с помощью postMessage() в главном windows.

. Проблема в том, что, хотя у меня открыто несколько вкладок, я получаю информацию только из последняя вкладка.

Вот мой код:

  1. Подготовка postMessage() слушатель в главном окне:

    window.addEventListener('message', (event) => { console.log(event.data); }, false);

  2. Создайте массив моих вкладок и получите элементы DOM:

    alert_list = document.querySelectorAll("tr.expand.alerts-table-tablerow"); var newTabs = [];

  3. L oop поверх элементов DOM, откройте вкладки , добавьте код JS, который возвращает в главное окно необходимые данные. Вот где происходит основная работа:

    alert_list.forEach((currentValue, currentIndex) => {
    alert_status = currentValue.childNodes[13].innerText;
    if(alert_status == "Enabled") {
        console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
        newTabs.push(window.open(currentValue.childNodes[5].children[0].href, "_blank"));
        newTabs[newTabs.length - 1].onload = function() {
            setTimeout(((tab_element) => {return () => {
                    window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
            }})(newTabs[newTabs.length - 1]), 120*1000);
        };
    }
    

    });

Теперь я продолжаю получать информацию из последней вкладки, которая открывается несколько раз. Точно количество вкладок, которые открыты. Это привело меня к мысли, что проблема заключалась в том, что javascript выполнял позднее связывание с обратным вызовом setTimeout, поэтому я изменил его на следующее, вдохновленное this :

((tab_element) => {return () => {
                    window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
            }})(newTabs[newTabs.length - 1])

Это фактически выполняет закрытие фактического элемента DOM, который я хочу. Но все равно не работает.

Что я делаю не так?

Дайте мне знать, требуется ли какая-либо другая информация.

Спасибо.

1 Ответ

1 голос
/ 28 марта 2020

setTimeout вызывается не синхронно, а после того, как сработало событие «load» вкладки, к этому времени forEach l oop завершил работу и оставил newTabs.length - 1 со ссылкой на последнюю вкладку.

Одним из решений может быть использование переменной для замены многократного использования length-1. Не проверено, но в соответствии с:

alert_list.forEach((currentValue, currentIndex) => {
    alert_status = currentValue.childNodes[13].innerText;
    if(alert_status == "Enabled") {
        // console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
        let tabWin = window.open(currentValue.childNodes[5].children[0].href, "_blank")
        newTabs.push( tabWin);
        tabWin.onload = function(event) {
            setTimeout( () => {
                window.parent.postMessage(
                    tabWin.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")
                    [0].innerText
                );
            }, 120*1000);
        };
    }
});

Вот оригинальный код с отступом, чтобы более четко показать, где происходит отложенная оценка (с объявлением, добавленным для alert_status:

alert_list.forEach((currentValue, currentIndex) => {
    let alert_status = currentValue.childNodes[13].innerText;
    if(alert_status == "Enabled") {
        console.log(currentValue.childNodes[3].innerText + " " + currentValue.childNodes[7].innerText);
        newTabs.push(window.open(currentValue.childNodes[5].children[0].href, "_blank"));
        newTabs[newTabs.length - 1].onload = function() {
            setTimeout(
                (
                    (tab_element) => {
                            return () => {
                                window.parent.postMessage(tab_element.document.querySelectorAll("h1.search-name.section-title.search-title-searchname")[0].innerText);
                            }
                    }
                )(newTabs[newTabs.length - 1])
            , 120*1000);
        };
    }
});

Анонимная функция onload компилируется в функциональный объект, когда она добавляется, но не выполняется до тех пор, пока не сработает событие load.После выполнения она создает «замыкание» для tab_element, принимая его значение из newTabs[newTabs.length - 1], которое является теперь последний элемент табуляции.

Решение состоит в том, чтобы удалить код трюка, который вызывает проблему.


Принцип работы замыканий в JavaScript - это topi c. Однако введение для целей объяснения этого ответа:
  • Когда вызывается функция, запись (объект в терминах JavaScript) используется для хранения значений переменных и функций используется внутри функции. Фактически, идентификаторы переменных теперь связаны с такой «записью среды». В ES3 и более ранних версиях в блоке не требовалось никаких дополнительных записей level (для операторов в фигурных скобках) или для особого случая for( let identifier... l oop, поскольку объявления let, const и class еще не введены. Одна запись среды в ES3 была названа «объектом активации».

  • Обычно, когда функция возвращается, записи среды, созданные для отдельного вызова, могут быть собраны сборщиком мусора на том основании, что они не могли этого сделать. больше должно быть достигнуто через код - текущий критерий для JavaScript сбора мусора в памяти (MG C).

  • Если и при этом значения вложенных функций и переменных могут если после выхода из функции они будут доступны в коде, они не могут быть удалены из памяти. Эта ситуация обычно описывается как функции и переменные, содержащиеся в «замыкании».

    1. Значение winVar в примере решения содержится в отдельной записи среды для каждого вызова forEach делает аргумент своей функции - различные значения, сгенерированные в последовательных вызовах, не перезаписывают друг друга.
    2. Функции onload и setTimeout callback являются разными объектами функций в отдельных замыканиях. Обратный вызов setTimeout имеет доступ к записям окружения своей родительской функции onload, которая имеет доступ к записям записи окружения своей родительской функции открытия вкладок.
    3. Ссылка tabWin в обратном вызове таймера в конечном итоге разрешается привязка идентификатора, содержащегося в записи среды аргумента функции forEach, открывшего окно вкладки при вызове.
...