document.readyState が loading のときのブラウザの振る舞い
結論
よくわからなかった。
やってみた
ブラウザがHTMLを読み込んでWebページを表示するまでにdocument.readyState
という値は loading
、interactive
、complete
の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.readyState
が loading
のときは画面は真っ黒で何も表示されていない。公式サイトによると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の挙動を知るにはこのあたりの本を読めばいいのかな?