空きっ腹のブルース

お腹が空いた冬

document.readyState が loading のときのブラウザの振る舞い

結論

よくわからなかった。

やってみた

ブラウザがHTMLを読み込んでWebページを表示するまでにdocument.readyStateという値は loadinginteractivecompleteの3つの状態をとる。リンクをクリックした直後はloadingで、Webページを表示し終えた後がcompleteという値になるらしい。ドキュメントに書いてあった。 もっと詳しく書くと次の表の通りである。

説明
loading この文書 (document) はまだ読み込み中です。
interactive 文書の読み込みが完了し、DOMにアクセスできる。ただしスクリプト、画像、スタイルシート、フレームなどのサブリソースはまだ読み込み中である。
complete 文書とすべてのサブリソースの読み込みが完了した。この状態は load イベントが発行されようとしていることを示しています。

拡張機能を開発するにあたってこのイベントの挙動を知りたかったので実際に動かして調査してみる。まずは利用するソースコードを書いておく。

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="styles.css" type="text/css">
    <title>サンプル</title>
</head>
<body>
    <h1 id="title">のサンプル</h1>
    <script src="script.js"></script>
</body>
</html>
body {
    font-family: Arial, sans-serif;
    background-color: gray;
    margin: 0;
    padding: 0;
}
const state = document.readyState;
alert(`${state}`);

このコードでは JS をscriptタグから読み込んで alert() でダイアログを表示させる。ダイアログに表示させる文字が今回知りたい document.readyState の値である。document.readyStateを呼び出した場所は1行目なのでHTMLからJSが読み込まれたら一番最初に取得している。その直後にalert()でダイアログを表示させてるのでJSの処理は止められるので、document.readyStateの状態をたもったままで調査ができる。

結果

上のコードを実行したらこうなった

document.readyStateloadingのときは画面は真っ黒で何も表示されていない。公式サイトによるとdocumentはまだ読み込み中なので確かに書いてある通りだ。OKボタンを押して次に進むとCSSで装飾されたページが返ってきた。

ちなみに再度読み込みをするとこうなる。

h1タグに書いた文字は無くなるが、背景が灰色のままである。これはキャッシュが残っているからだ。キャッシュを削除して再度読み込みするには「検証」を開き、ブラウザの再度読み込みボタンを右クリックして出てくる「キャッシュの削除とハード再読み込み」という項目をクリックするとできる。

実は再度読み込みは3つのモードがあって、Ctrl + Rでできる再読み込みはキャッシュを削除しない一番弱いモードなのだ。ちなみにこの再読み込みにショートカットはなく、検証を開いてないとできない。検証を開いてない状態で再度読み込みボタンを右クリックしても何も表示されないから注意。

さて、loadingのときにDOMにアクセスできるのだろうか。JSを次のコードに変えて実行してみる。

state = document.readyState;
// HTMLに直接書き込んである要素を取得
existing_elem = document.body.getElementsByTagName("h1")[0].innerHTML;

alert(`stateは${state} \n h1の内容は ${existing_elem}`);

なんとDOMにアクセスできてしまった。公式サイトの動作確認の例では DOM はまだ構築されていないように書いてあったのに。。。HTMLに書いてアクセスあるからで、JSで生成したDOMはどうなるかと思って調べた。

// readyState の状態を取得
state = document.readyState;
// HTMLに直接書き込んである要素を取得
existing_elem = document.body.getElementsByTagName("h1")[0].innerHTML;

// js でspan要素を作成
const span = document.createElement("span");
span.textContent = "spanがJSで作成されてるよ";
document.body.appendChild(span);
injected_elem = document.body.getElementsByTagName("span")[0].innerHTML;

// const styles_count = document.styleSheets.length;

// alert(`[${state}]  ${existing_elem} -- ${injected_elem} -- ${styles_count}`);
alert(`stateは${state} \n h1の内容は ${existing_elem}`);

なんとJSで生成したDOMにもアクセスできてしまった。このまま読み込んだCSSファイルの個数も調べてみたら、やっぱり読み込めているようだ。

// readyState の状態を取得
state = document.readyState;
// HTMLに直接書き込んである要素を取得
existing_elem = document.body.getElementsByTagName("h1")[0].innerHTML;

// js でspan要素を作成
const span = document.createElement("span");
span.textContent = "spanがJSで作成されてるよ";
document.body.appendChild(span);
injected_elem = document.body.getElementsByTagName("span")[0].innerHTML;

// 読み込んだ css ファイルの個数
const styles_count = document.styleSheets.length;
alert(`[${state}]  ${existing_elem} -- ${injected_elem} -- ${styles_count}`);

alert(`stateは${state} \n h1の内容は ${existing_elem} \n spanの内容は ${injected_elem} \n cssの個数は ${css_count}`);

これは思っていたのと違う挙動になってしまった。はじめに書いた表をもう一度読んでほしい。DOMにアクセスできるのは interativeのときであって、しかもスクリプトスタイルシートが読み込み完了するのは completeのときである。なのに loading状態で DOMにもアクセスができ、スタイルシートも読み込めてしまった。

説明
loading この文書 (document) はまだ読み込み中です。
interactive 文書の読み込みが完了し、DOMにアクセスできる。ただしスクリプト、画像、スタイルシート、フレームなどのサブリソースはまだ読み込み中である。
complete 文書とすべてのサブリソースの読み込みが完了した。この状態は load イベントが発行されようとしていることを示しています。

だとしたらコードが間違っているのだろうか。例えば、alert()はJSファイルで書いているが、上の表によるとスクリプトの読み込みが完了するのはcompleteのときだそうだ。だからJSを実行できるのはcompleteになったタイミングだから、そもそもJSではdocument.readyStateの値は全てcompleteになるはずだ。でもalert()で表示されてる値は確かに loadingなのだから、JSが実行された時点ではloadingなのだろう。alert()をHTMLのscriptタグに直接書き込んでも結果は同じだった。

そもそもグローバルオブジェクトのdocumentにアクセスできてる時点で、内部ではDOMが構築し終えてるのでは?というかdocumentっていつ生成されて、なんの情報を持つんだ?分からないまま、時間が過ぎ、まあいいかと思い調査を諦めた。作る拡張機能は単純なものだから分からなくていいこともある。いつか調べよう。

HTTPの挙動を知るにはこのあたりの本を読めばいいのかな?

https://www.amazon.co.jp/dp/4873119030/

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化 | Ilya Grigorik, 和田 祐一郎, 株式会社プログラミングシステム社 |本 | 通販 | Amazon