Прикрепление слушателя DOMContentLoaded к динамически создаваемому окну

Из-за того, как работают некоторые из наших страниц, JS может быть введена на страницу в любой момент, и иногда это JS закрывает текущее окно. Проблема в том, что мне нужно подключить прослушиватель событий к onunload окна, чтобы значение могло быть возвращено из окна на родительскую страницу. Но поскольку окно закрытия script может быть введено в любую точку, я не могу привязать это событие к onload из-за того, как он работает, поэтому я надеялся использовать DOMContentLoaded, поскольку это событие будет срабатывать до того, как будет введено script.

Однако в моих тестах я не могу ничего привязать к DOMContentLoaded на родительской странице, где создается новое окно.

Вот что я сейчас работаю с: Plunker

Нам нужно только это, чтобы работать в Chrome на данный момент.

Наш текущий метод работы работает так (псевдокод):

onButtonClick = function(){
    win = window.open(...);
    win.onload = function(){
        win.onunload = function(){
            //Bind some function that will get the window "return value" and pass it to the parent page
            //This will never happen if the window closes itself before the page is done loading
        };
    };
};

Можно ли использовать DOMContentLoaded для выполнения того, что я хочу? Если да, как я правильно прикрепляю его к окну?

Примечание. Я не могу привязать событие onunload непосредственно к окну после его создания. Кажется, он дважды запускает событие onunload (один раз, когда открывается окно и один раз, когда он закрывается). Это можно увидеть, если вы используете функцию "bindOnCreate" в моем примере.

Ответ 1

Если вы меняете строку 58 из

w.document.addEventListener('DOMContentLoaded', ...)

к

w.addEventListener('DOMContentLoaded', ...)

он работает. Я попытаюсь объяснить, что на самом деле происходит под капотом:

  • Когда окно открыто, изначально он имеет URL about:blank. Вы можете проверить это, зарегистрировав w.location.toString() в обработчике событий onunload (см. Следующий шаг).
  • Сразу после этого браузер загружает URL-адрес, указанный в window.open, тем самым вызывая onunload для about:blank (первый раз).
  • В всплывающее окно загружается реальная страница с различным окном. Документ загружается во всплывающее окно, но ваши обработчики событий по-прежнему прослушивают корень DOM страницы about:blank, потому что вы добавили события в window.document, а не window; и прямо сейчас, когда у нас загружен другой URL-адрес, window.document - это совершенно другой объект, чем один шаг раньше.
  • Когда вы закрываете окно, onunload снова запускается (второй раз), потому что ваше событие onunload было подключено к окну .

Если вы addEventListener для всплывающего окна window, он получает события из всех window.document -s, которые будут загружены внутри этого окна из-за механизма барботирования событий JS.

Я надеюсь, что это ответит на ваши вопросы.

Ответ 2

В качестве альтернативы вы можете отправить сообщение из дочернего окна в его открыватель, вместо того чтобы позволить открывающему устройству обработать событие выгрузки дочернего окна. Это будет намного проще, и вам не нужно беспокоиться о точке инъекции. Также вы можете избавиться от дважды выгружаемого события, поскольку Эндрю Дунай уже дал причину этой проблемы.

Здесь я даю очень простое демо, только показывает скелет моего решения для обмена сообщениями:

parent.html

<button id="open">Open Window</button>
<button id="close">Close Window</button>
<div></div>
<script>
  var child;

  document.querySelector('#open').addEventListener('click', function() {
    document.querySelector('div').innerHTML = '';
    child = window.open('child.html', 'child', 'width=600,height=600');
  }, false);

  document.querySelector('#close').addEventListener('click', function() {
    child.close();
  }, false);

  window.addEventListener('message', function(e) {
    document.querySelector('div').innerHTML = e.data;
  }, false);
</script>

child.html

<input type="text" value="" placeholder="input something">
<script>
  window.addEventListener('beforeunload', function() {
    var msg = document.querySelector('input').value;
    window.opener.postMessage(msg, '*');
  }, false);
</script>

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

Это ключевой подход для одного из моих побочных проектов и работает очень хорошо.