はじめに
本記事ではWebブラウザによってHTML 解析時にJavaScript がどのような流れで実行されるのかをまとめます。
WebブラウザがHTMLを受け取ってから、リソースの読み込みが完了したことを表すloadイベントが発生するまでの流れを見ていきます。
HTML の解析
レンダリングエンジン1のHTML パーサーが、受け取ったHTML を解析しながらDocument オブジェクトを作成していきます。
JavaScript エンジンからこのDocument オブジェクトに要素を追加・削除・更新する際には、getElementByIdなどのDOM API を通じて行います。
スクリプトの読み込み・実行
HTML パーサーは「async,defer,type="module"」のない<script>要素をみつけるとドキュメント(DOM ツリー)に追加し、スクリプトを実行します。
このスクリプトで参照できるDocument オブジェクトは、すでに構築済みのものに限られます。
<script>要素よりもあとに記述されている要素にgetElementByIdなどでアクセスすることはできないわけです。
スクリプトがダウンロード、実行されている間、HTMLパーサーによる解析は停止し、DOMツリーの構築がブロックされる点には注意が必要です(レンダリングブロッキング)。
document.write()によってHTML ドキュメントに直接HTML文字列が追加される可能性があるため、解析を停止する必要があるわけです。
deferやasyncの使用時にはレンダリングブロッキングは起こりません。
async属性
asyncをもつ<script>要素については、スクリプトのダウンロードが非同期的に行われ、実行されます。
非同期実行のため、<script>要素の後ろにあるdocumentオブジェクトにアクセスできる場合もあります。
HTMLの解析の完了
HTML ドキュメントの解析が完了し、DOM ツリーの構築が完了すると2、「defer,type="module"」をもつ<script>がHTML に記述された順番に実行されていきます。
これらのスクリプトはDOM ツリーの構築完了後に実行されているため、ドキュメントツリー全体にアクセスできます。
これらの<script>の同期処理が完了すると、ブラウザはDocument オブジェクトに対してDOMContentLoadedイベントを発生させます。
この時点では画像やCSS などリソースの読み込みが完了していません。
asyncをもつスクリプトはこの時点で実行が完了していない場合もあります。
loadイベントの発生
リソースの読み込みやasyncスクリプトの読み込み、実行がされたときにブラウザはWindow オブジェクトに対してloadイベントを発生させます3。