Асинхронно загруженные скрипты с обработчиками событий DOMContentLoaded или load не вызываются?
у меня есть скрипт DOMContentLoaded
обработчик-
document.addEventListener('DOMContentLoaded', function() {
console.log('Hi');
});
который я загружаю асинхронно -
<script async src=script.js></script>
однако обработчик событий это не называется. Если я загружаю его синхронно -
<script src=script.js></script>
он работает нормально.
(даже если я изменю DOMContentLoaded
событие load
событие, оно никогда не вызывается.)
что это дает? Обработчик события должен быть зарегистрирован независимо от того, как скрипт загружен браузер, нет?
редактировать: он не работает на Chrome 18.0.1025.11 beta, но с DOMContentLoaded
, это тут в бета-версии Firefox 11 (но с load
это не так). Иди разберись.
О ВЕЛИКИЕ ВЛАДЫКИ JAVASCRIPT И DOM, ПОЖАЛУЙСТА, ПОКАЖИТЕ ОШИБКУ МОИХ ПУТЕЙ!
4 ответов
загружая скрипт асинхронно, вы сообщаете браузеру, что он может загружать этот скрипт независимо от других частей страницы. Это означает, что страница может завершить загрузку и может выстрелить DOMContentLoaded
перед загрузкой скрипта и перед регистрацией события. Если это произойдет, вы пропустите событие (это уже произошло, когда вы регистрируетесь на него).
в некоторых браузерах, вы можете проверить, чтобы увидеть, если он уже загружен. Я не проверил все совместимость браузера, но в Firefox 3.6+ (MDN doc), вы можете увидеть:
if (document.readyState !== "loading")
чтобы увидеть, если документ уже загружен. Если это так, просто делай свое дело. Если это не так, установите прослушиватель событий.
на самом деле, как ссылочный источник и идея реализации, jQuery делает то же самое с ним .ready()
метод и он выглядит широко поддерживается. jQuery имеет этот код после .ready()
- Это называется, что сначала проверяет, если документ уже нагруженный. Если это так, он немедленно вызывает функцию ready, а не привязывает прослушиватель событий:
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout( jQuery.ready, 1 );
}
Это не окончательный ответ, но заставил меня понять, почему неправильно использовать async со скриптом, который нужно изменить DOM, поэтому нужно дождаться события DOMContentLoaded. Надежда может быть полезной.
(источник: запуск кода в нужное время из kirupa.com)
один из способов обойти это-использовать событие load для объекта window.
это произойдет позже, чем DOMContentLoaded, но, по крайней мере, вам не придется беспокоиться о пропущенном событии.
window.addEventListener("load", function () {
console.log('window loaded');
});
Если вам действительно нужно поймать событие DOMContentLoaded, вы можете использовать объект Promise. Обещание разрешится, даже если это произошло раньше:
HTMLDocument.prototype.ready = new Promise(function (resolve) {
if (document.readyState != "loading")
return resolve();
else
document.addEventListener("DOMContentLoaded", function () {
return resolve();
});
});
document.ready.then(function () {
console.log("document ready");
});
имитация медленного подключения 3G используя Chrome devtools у меня были проблемы с манипулированием DOM из внешнего async
сценарий, однако этот фрагмент, который я нашел где-то на GitHub решил это за меня:
var DOMReady = function(callback) {document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);};
DOMReady(function() {
//DOM is ready!
});
может ли кто-нибудь поручиться за это?
как это соотносится с уже предложенных решений?
Я также нашел аналогичное решение здесь:
https://javascript.info/onload-ondomcontentloaded#readystate