scriptのasync属性とdefer属性の挙動

MDNによるとscriptのdefer属性は

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

と書かれているが、文書の解析完了と言う言葉が具体的にどういう意味を持つのかわからない。非deferなスクリプトでDOM操作を行う場合、操作したい要素より後に読み込むか、DOMContentLoadedイベントハンドラで行うのが作法となっている。deferの文書の解析完了とはDOMに自由にアクセスして良いという意味と同義なのか、ちょっと確証がない。

この手の情報をネットでググってもお互いにコピペしてんじゃないかと思える判で押したような記事ばかり、まるで役に立たないので、自分で実際の挙動を調べた。結論はDOM操作してOKである。

default async defer
後続処理のブロック する しない しない
実行タイミング 要素が現れた時点で 不定 文書の解析完了後からDOMContentLoaded発火の間
同一属性スクリプトの実行順 HTMLに現れた順番で 不定 HTMLに現れた順番で
DOM要素の操作可否 自分より前の要素またはDOMContentLoadedで 自分より前の要素 全てOK
DOMContentLoadedイベント ハンドリング可能 ハンドリングできない事がある ハンドリング可能

deferについてMDNにはこうも書かれている

defer 属性の付いたスクリプトは、スクリプトが読み込まれて評価が完了するまで、 DOMContentLoaded イベントの発生が抑制されます。

これは、ブラウザ上でドキュメント表示は完了しているのに、JS実行が終わっておらずWebアプリケーションとして動作しないケースがあり得るという事。まぁ、deferのスクリプトのダウンロードと実行だけが異常に遅いというケースは、スクリプトが無限ループしてしまったなどの極端な場合を除けば考えにくいので気にする必要はないだろう。が、頭の隅にでもいれておいて損はないと思う。

async属性付きのスクリプトは実行タイミングが不定のため、他のスクリプトと依存関係を持つコードが書けない。1つで完結したスクリプト、基本的には外部スクリプトの読み込み用途がメインになるだろう

<div id="ここにwidgetが表示"></div>
<script async src="http://example.com/external/widget.js"></script>

このようなケースでスクリプトを同期実行すると、せっかく構築が進んだDOMとCSSOMの再構築が必要になりファーストビュー表示が遅延するので、asyncをつけるのが有効らしい。