web-dev-qa-db-ja.com

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の場合は機能しません)。

ジャバスクリプトとDOMの偉大な主よ、祈りは私の道の誤りを示します!

36
user1203233

スクリプトを非同期でロードすることにより、ページの他の部分とは独立してそのスクリプトをロードできることをブラウザに伝えます。つまり、スクリプトが読み込まれる前で、イベントに登録される前に、ページの読み込みが完了し、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 );
}
_
60
jfriend00

これは最終的な回答ではありませんが、DOMを変更する必要があるスクリプトで非同期を使用することが正しくない理由を理解させたので、DOMContentLoadedイベントを待つ必要があります。希望は有益かもしれません。

enter image description here

(ソース: 適切なタイミングでコードを実行するには、kirupa.comから

24

これを回避する1つの方法は、windowオブジェクトでloadイベントを使用することです。

これは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");
});
3
user4617883