Подтвердить что ты не робот

Асинхронные сценарии с обработчиками событий DOMContentLoaded или load не вызываются?

У меня есть script с обработчиком событий DOMContentLoaded -

document.addEventListener('DOMContentLoaded', function() {
    console.log('Hi');
});

Я загружаю асинхронно -

<script async src=script.js></script>

Однако обработчик события никогда не вызывается. Если я загружу его синхронно -

<script src=script.js></script>

Он отлично работает.

(Даже если я изменил событие DOMContentLoaded на событие load, он никогда не вызывал.)

Что дает? Обработчик событий должен регистрироваться независимо от того, как браузер загружается script, нет?

Изменить. Он не работает на бета-версии Chrome 18.0.1025.11, но с DOMContentLoaded он работает на бета-версии Firefox 11 (но с load это не так). Наведите указатель мыши.

ОН БОЛЬШИЕ ЛОРДЫ JAVASCRIPT И ДОМА, МОЛИТЬ ПОКАЗАТЬ ОШИБКУ МОИХ ПУТЕЙ!

4b9b3361

Ответ 1

Загружая script асинхронно, вы сообщаете браузеру, что он может загрузить этот script независимо от других частей страницы. Это означает, что страница может завершить загрузку и может запустить DOMContentLoaded до загрузки вашего script и до того, как она зарегистрируется для события. Если это произойдет, вы пропустите событие (это уже произошло, когда вы зарегистрировались для него).

В некоторых браузерах вы можете протестировать документ, чтобы узнать, загружена ли она. Я не проверял совместимость браузера, но в Firefox 3.6+ (MDN doc) вы можете проверить:

if (document.readyState !== "loading")

чтобы узнать, загружен ли документ. Если это так, просто делайте свое дело. Если это не так, установите установщик событий.

Фактически, как исходный источник и идея реализации, jQuery делает то же самое с этим методом .ready(), и он широко поддерживается. jQuery имеет этот код, когда вызывается .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 );
}

Ответ 2

Это не окончательный ответ, но я понял, почему неправильно использовать асинхронный с script, который должен изменить DOM, поэтому должен ждать событие DOMContentLoaded. Надежда может быть полезной.

введите описание изображения здесь

(Источник: Выполнение кода в нужное время с сайта kirupa.com)

Ответ 3

Большинство ванильных функций JS Ready НЕ учитывают сценарий, когда обработчик DOMContentLoaded запускается после того, как документ уже загружен, что означает, что функция никогда не будет запущена. Это может произойти, если вы используете DOMContentLoaded во внешнем скрипте async (<script async src="file.js"></script>).

Приведенный ниже код проверяет DOMContentLoaded, только если документ readyState еще не interactive или complete.

var DOMReady = function(callback) {
  document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback());
};
DOMReady(function() {
  //DOM ready!
});

Если вы хотите поддержать IE, а также:

var DOMReady = function(callback) {
    if (document.readyState === "interactive" || document.readyState === "complete") {
        callback();
    } else if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", callback());
    } else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", function() {
            if (document.readyState != "loading") {
                callback();
            }
        });
    }
};

DOMReady(function() {
  // DOM ready!
});

Ответ 4

Один из способов - использовать событие загрузки в объекте 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");
});