Прикрепление слушателя 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>
Либо вы закроете дочернее окно, щелкнув его собственную кнопку закрытия окна, либо кнопку в родительском окне, значение в дочернем входе всегда будет отправлено родительскому и представлено.
Это ключевой подход для одного из моих побочных проектов и работает очень хорошо.