なぜhead要素内で外部JavaScriptファイルを読み込むのが良くないのか?

PCの画面
JavaScriptのコードをHTMLとは別ファイルとして保存し、HTMLからリンクさせて読み込むことを「外部ファイル化する」と呼んだりします。

その際、以下のようにHTMLのhead要素内から読み込むことには大きなデメリットが実はあるのです。

<html>
<head>

<script src="ここにJavaScriptファイルへのパス"></script>

</head>
<body>


</body>
</html>

このページではそのデメリットと理由や解決法について解説していきます。

2019年6月18日追記:このページの内容な誤解されるような表現が多かったため、大幅に書き直しました。
間違いがありましたらご連絡ください。

Webブラウザがページを画面上に描画するためには、まずHTMLを読み込んで解析する必要があります。
この解析機能のことは「パーサー」と呼ばれたりします。

パーサーはHTMLファイルの一番上から順番に解析していき、その途中に外部のJavaScriptファイルがあれば、その時点でそのJSファイルを読み込みに行きます。

そして、外部JSファイルを読み込み・実行されるまでの間はそれ以降のHTMLの解析がストップさせられるため、head要素内にそのような外部ファイルがあると結果的にページが表示されるのがそのぶん遅くなります。

最近のPCやスマホの性能の高さから考えると、そのJSの実行時間よりもHTTP通信が1本(外部ファイルが1つの場合)発生することによる遅延のほうが大きいと思われます。

JavaScriptの外部ファイルの読み込みはhead要素内に限らずbody要素内に書くことも認められています。

<html>
<head>

</head>
<body>


<script src="ここにJavaScriptファイルへのパス"></script>
</body>
</html>

上記のようにbody要素の最後から外部のJavaScriptファイルを読み込むことで、HTMLの解析がストップされることなく最後(外部JSの読み込みがある位置)までいきます。
HTMLの解析が最後まで行くということは、

  1. そこまでのHTMLのDOM構築は完了し、構築できている分は画面上に表示されている。(構築処理にかかる時間は今回は考慮しません)
  2. そこまでのHTMLにCSSファイルや画像ファイル(img要素)の読み込みコードがあれば、ダウンロードを開始できる。

ということになります。
これがもしhead要素内でパース(解析)がストップした場合は、body要素内のDOM構築は一切できておらず、画像ファイルのダウンロードも開始されません。

一般的なページ構造であれば、ページを表示するために最も優先されるべきものはHTMLファイルとCSSファイルですから、そちらをなるべく早い段階からダウンロード・解析させて、一瞬でも早くDOMCSSOMを構築させたほうがページ表示のタイミングが早くなります。

補足説明:ブラウザがページを画面上に表示するためには、HTMLを読み込んで「DOMツリー」を、CSSを読み込んで「CSSOMツリー」というものを内部的に構築する必要があります。

たとえトータルでダウンロードするファイルの数やサイズは同じだとしても、画面上に表示されるタイミングが一瞬でも早いほうが体感速度が早まるのです。

そのような理由から、ページの表示後に読み込み・実行しても問題ない内容(アクセス解析ツールなど)のJavaScriptファイルはhead要素内で読み込むのではなく、body要素の最下部に書いておいたほうが良いでしょう。

木
実際のところ、head要素内でJavaScriptコードを読み込んだとしても、そのコード内容が実行されるタイミングはページ表示後というケースが多いです。

なぜなら、JavaScriptのコードではreadyonloadといった関数が使用されるのが多く、これらの関数によって実行タイミングが「DOMツリー構築後(ready)」とか「ページの表示後(onload)」などにされているからです。

「DOMツリー構築後って何だ?」と思った方もいると思いますが、ここでは大ざっぱに「ページ表示後とだいたい同じ」と考えて良いです。
つまり、head要素内で外部JavaScriptファイルを読み込むと、

外部JavaScriptファイルを読み込んでいる間はそれ以降のHTML解析が止められるけども、そのJavaScriptの内容の実行はページ表示後に行われる。

という全く無駄な状況が生まれてしまうのです。
というわけで、外部JavaScriptファイルの読み込みはbody最下部に書くようにしましょう。

ページ表示速度にうるさいGoogleもそれを推奨しています。
参考リンク:レンダリングを妨げる JavaScript を削除する  |  PageSpeed Insights |  Google Developers

WordPressでjQueryをフッターで読み込ませたい場合は「WordPress同梱のjQueryをGoogle CDNのものに変更してwp_footer()で読み込む方法」のページで解説しています。

外部JSファイルを読み込むHTMLコードは通常は以下のようになりますが、

<script src="ここにJavaScriptファイルへのパス"></script>

以下のようにdeferasyncを付けておくことで、HTMLの解析をストップさせることなく、このJSを読み込み・実行させることができます。
この場合はhead要素内に以下のコードを書いたとしても問題ないでしょう。

<script src="ここにJavaScriptファイルへのパス" defer></script>
<script src="ここにJavaScriptファイルへのパス" async></script>

deferとasyncのどちらが良いかと言えば、ハッキリ言ってasyncのほうが優れています。
がしかし、deferとasyncはその内容の実行タイミングが異なるため、よくわからない場合や正常動作の検証ができない人はdeferにしておくほうが無難です。

jQueryのような依存関係のあるファイルが存在しない」とか「DOMを操作しない」ような内容のJSファイルであれば、asyncでも問題ないと思います。

提供サポートなど