Edited at

自作JSの読み込みはbodyの最後に書いたほうがいいのに、なんで外部ライブラリはheadにあるんでしょうか?


調べるキッカケ

最近フロントエンドを触り出して、JQueryの勉強(初歩中の初歩)をしていたのですが、

「JSの読み込みはbodyの最後に」という決め事があるようで、ちょっと気持ち悪いので調べていました。

すこしググると見解として、


HTML中にJavaScriptファイルを読み込む記述があると、そのJavaScriptを読み終わるまで、ウェブページのレンダリング(描画)が止まってしまうからです。

https://allabout.co.jp/gm/gc/380498/2/


などと記載されていて、なるほどと思ったのですが、

Qiitaのページも含め、ざっと確認したページではJSライブラリはすべてheadに書かれており、

スクリーンショット 2019-05-10 23.17.31.png

なぜ「はじめからJSを全部bodyの最後に置かないんだろう?」と思い、調べてみました。


結論

bodyの最後になんて書かず、deferオプションを使おう。


経緯

レンダリングが遅くなってしまう件について、

Googleさんの公式ページに見解が載っていました。


jQuery などの JavaScript ライブラリを使用している場合はどうすればよいですか?

JQuery など多くの JavaScript ライブラリは、インタラクティブ性やアニメーションなどの効果を付加してページの魅力を高めるために使用されています。ただし、こうした動作の多くは、スクロールせずに見える範囲のコンテンツが表示された後に追加しても差し支えありません。そうした JavaScript を非同期にする、または読み込みを遅らせることを検討してください。

https://developers.google.com/speed/docs/insights/BlockingJS?hl=ja


要約すると以下になります。


  • JavaScript を非同期にする

  • スクロールせずに見える範囲のコンテンツが表示された後(≒bodyの最後)に読み込む

なるほど。たしかにJavaScript を非同期にするために、

Qiitaのページではasyncオプションを使ってました。

例)

<script src="https://securepubads.g.doubleclick.net/gpt/pubads_impl_2019043001.js" async=""></script>

また、いろいろ調べていると、deferオプションというものを使って、HTMLレンダリング後に読み込みを実施しているようでした。

例)

<script src="https://cdn.qiita.com/assets/public/bundle-d8e605d584dac63090b5a3f765cc8dbf.min.js" defer="defer"></script>

要するに、JSをbodyの最後に書かなくても、deferオプションを追加すると、HTMLのパースが完了したあとにJSを実行してくれるらしいです。


async

これは論理属性で、可能であればスクリプトを非同期で読み込むべきであることを示します。

defer

この論理属性は、スクリプトを文書の解析完了後かつ DOMContentLoaded が発生する前に実行することをブラウザーに示します。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/Script#Attributes


 

なので、実際にライブラリを置くなら、


  • とりあえず全部headに置いて、

  • Google Analyticsなど処理負荷が大きそうなものはasyncで、

  • 自作のJSは内部処理に応じてインラインにしたり、deferオプションにして分散する

がよさそうです。


補足


deferオプションについて

ブラウザによってはdeferオプションが効かないようです。

これが、このオプションがまだ一般的なものとして普及していない理由かもしれません。

(まあ古いブラウザなら遅くても我慢するような人間が使っている気もしますが。。)

名称未設定2.png

※引用:https://developer.mozilla.org/ja/docs/Web/HTML/Element/Script#Browser_compatibility (20190510時点)

Chromeのバグで、

XHTML形式のページだと効かないこともあるようです。


補足 Chrome does not defer scripts with the defer attribute when the page is served as XHTML (application/xhtml+xml)

https://bugs.chromium.org/p/chromium/issues/detail?id=874749

https://bugs.chromium.org/p/chromium/issues/detail?id=611136


 

また、deferオプションは、JSの「実行」のみを遅らせるため「ダウンロード」時間はHTMLパースに影響を与えます。

(とはいえ、ダウンロード自体は非同期処理であり、headの段階で読み込んでくれるため、bodyの最後に配置するより全体的な速度は速くなるはずです。)

もしHTMLの表示速度が遅く、JSのファイルサイズが大きい場合は、(自作JSに限らず外部ライブラリも含め)bodyの最後に配置し、ダウンロードの影響をなくす検証をしてみます。


HTMLのパース後にJSを読み込むことが正とも限らない

もしスクロールをしないとコンテンツが出現しないようなサイトを構築する場合には、一度確認してみる必要がありそうです。


追記:JavaScriptの読み込み位置をページ最後にしない方がよい場合

スクロールをしないとコンテンツが出現しないようなサイトの作りの場合にJavaScriptをページ最後で読み込むようにすると、JavaScriptの読み込みが終わるまで、下のコンテンツへ勧めなくなってしまう

https://memocarilog.info/jquery/5842