【JavaScript】 script asyncはDOMContentLoadedを捕捉できない
更新日:2020/06/18
html内で次のようにスクリプトをasync属性で呼び出すと、非同期で読み込まれる。
<script async src="https://xxxx.com/script"></script>
非同期で読み込まれることで、ブラウザのファーストビューのレンダリングが高速化されるというメリットがある。
しかしasync属性で呼び出したスクリプトは、DOMContentLoadedイベントを捕捉できるときと、できないときがある。
非常に対処に困る事例なので、対処法を考えてみた。
asyncとDOMContentLoaded
DOMContentLoadedイベントは、htmlを読み込み、DOMの構築が終わったタイミングで発生します。
このイベント以前はDOMが存在しないため、htmlタグなどの操作ができない。
そのため次のように、document.addEventListener()でイベント発生を待つ必要があります。
document.addEventListener( "DOMContentLoaded" , ()=>{ } );
DOMContentLoadedは、次のようにscriptタグにasync属性を指定しない場合問題なく補足できます。
<script src="https://xxxx.com/script"></script>
html内にscriptタグが現れた時点では、htmlを最後まで読み込んでいないので、DOMContentLoadedは発生していません。
このタイミングでスクリプトファイルが読み込み、実行するのでイベントを捕捉できるのです。
一方async属性を付与すると、html読み込みと並列でスクリプトを読み込みます。
そして、スクリプトの読み込みが完了した時点で実行されます。
DOMの構築状況は考慮されません。
そのため同一のスクリプトファイルであっても、DOMContentLoadedイベント発生前に実行されるケースもあれば、イベント発生後にスクリプトが実行されるケースもあるのです。
つまりasync属性を付与したスクリプトで確実にDOMContentLoadedイベントを捕捉する方法はありません。
対処法
defer属性で読み込む
次のようにdefer属性を指定したスクリプトは、asyncと同じように並列で読み込まれます。
<script defer src="https://xxxx.com/script"></script>
ただし実行タイミングが、異なります。
htmlを読み込みDOMを構築した時点で、実行されます。
全てのdefer属性を指定したスクリプトが実行されたあと、DOMContentLoadedイベントが発生します。
そのため、defer属性はDOMContentLoadedイベントが確実に発生します。
※defer属性はhtml内であらわれた順に実行されます。そのため依存関係を確保することができます。
DOMContentLoadedイベント発生前かどうか確認する
DOMContentLoadedイベント発生前かどうか確認し、発生後なら処理続行。
発生前なら、addEventListener()でイベントを登録します。
発生前なら、document.readyStateプロパティが"loading"となるので、これを利用します。
// DOM構築後に実行する関数
const myEvent = () => { };
document.readyState !== "loading" ? myEvent() :
document.addEventListener( "DOMContentLoaded" , myEvent );
更新日:2020/06/18
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。