「JavaScript/XMLHttpRequest」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
→‎XMLHttpRequest: イベントリスナーによるイベントハンドリング
タグ: 2017年版ソースエディター
 
32 行
 
== XMLHttpRequest ==
=== サンプルコードおよび前提の解説 ===
まず、これらの機能は、webサーバを仲介して行う必要がある。よってコードをテストするには、まずwebサーバ(ApacheやXAMPPなど)を立ち上げて、そこのドキュメントルートで行う必要がある。ドキュメントルートの説明については、『[[PHP/確実に動作させるまで]]』に説明しておいたので、webサーバに詳しくなければ参照してもらいたい。(もしwebサーバの使い方が全く分からない場合、JavaScriptの学習はいったん中断して、上記リンク先の[[PHP]]の単元を先に学習したほうが良いだろう。)
 
とりあえず、サーバの設定や構成の準備がうまく行ってるかどうかを、次のコードで試してみよう。下記プログラムは、XMLHttpRequestの動作確認用のプログラムである。(本来なら動作確認のほかに、サーバとのデータ送受信などを行いたいのだが、コードが長くなるので、今回はまだ動作確認だけにしている。)
 
ただし、アクセス先のテキストファイルとして hello.txt を test.html と同じディレクトリに用意してもらいたい。
今後の作業のため、hello.txt には「Hello world!」と書いておく(単にテキストファイルや画像を読み込むだけなら HTML の通常の機能でも可能だが、今回は動作確認なので、そのような単純な事例にしている)。
なお、実用の場合には サーバのPHPプログラム(rubyなど別言語でも構わない)と、もっと複雑な通信をしたりするだろう。
 
;test.html:<syntaxhighlight lang="html5" line>
<!DOCTYPE html>
<html>
<head>
<title>XMLHttpRequest</title>
<script>
const req = new XMLHttpRequest();
req.responseType = 'text';
 
const ReadyStateStrings = []
for (const property in XMLHttpRequest) {
ReadyStateStrings[XMLHttpRequest[property]] = property
}
console.log(ReadyStateStrings)
var start = Date.now()
for (const event in ["loadstart", "load", "loadend", "error", "abort", "timeout"]) {
req.addEventListener(event, ev => document.body.innerHTML += `<div>${Date.now() - start}(ms): event: ${event}<\/div>`)
}
req.addEventListener("readystatechange", (ev) => {
document.body.innerHTML += `<div>${Date.now() - start}(ms): status = ${req.status}, readyState = ${ReadyStateStrings[req.readyState]}<\/div>`
 
if (req.readyState === 4 && req.status === 200) {
document.body.innerHTML += `<div>${Date.now() - start}(ms): hello.txt => ${req.responseText}<\/div>`
}
});
req.open('GET', './hello.txt');
req.send(null);
</script>
</head>
<body>
<h1>XMLHttpRequest</h1>
</body>
</html>
</syntaxhighlight>
;実行結果(ブラウザ):<syntaxhighlight lang="html">
XMLHttpRequest
41(ms): status = 200, readyState = HEADERS_RECEIVED
41(ms): status = 200, readyState = LOADING
43(ms): status = 200, readyState = DONE
43(ms): hello.txt => Hello world!
</syntaxhighlight>
 
=== ミスの原因 ===
通信に成功せずに表示結果が上記の成功例と異なる場合、なんらかの失敗をしている。
 
;よくあるミス
・ダブルクリック起動してしまっているミス<br>
失敗する場合、原因は色々とありうるが、特にあるミスとして、 [http://localhost/test.html http://localhost/test.html ] のアクセスではなく、htmlファイルを直接ダブルクリックして起動してしまっていないかを注意してみよう。
 
単にドキュメントルートの中にhtmlを入れて、そのhtmlファイルをダブルクリック起動しても、失敗する。
 
必ず、ドキュメントルートにhtmlファイルを入れた上で、さらに必ず [http://localhost/test.html http://localhost/test.html ] にアクセスしているかを確認しよう。
 
・サーバ立ち上げていないミス<br>
また、そもそもwebサーバが立ち上がっていない場合も当然ながら上記コードの動作確認には失敗するので、ローカルホスト自体[http://localhost http://localhost]にもアクセスしてみて、確認してみよう。サーバが立ち上がっているなら、webサーバ(XAMPPまたはApacheなど)の用意している画面(ロゴマークなどがある画面)が表示されるハズであるので、その画面が表示されるか確認してみよう。
 
=== 技術的な解説 ===
==== HTTPコマンド ====
「GET」とは、HTTPコマンドの一種です。HTTPコマンドには、主にデータ取得を目的とするために通信する「GET」と、データ送信のために通信する「POST」という、2種類があります。
 
なお、GETもPOSTもどちらとも、相手先サーバとの通信のために、送信をしています。このため、原理的には「GET」で送信をすることも可能です<ref>山田、P398</ref>。もっとも、なるべく本来の目的にあったHTTPコマンドを選んで使うほうが良いプログラムではあるでしょう。
 
また、GETコマンドは送信URLの末尾にデータを付加して
?キー名=値&
の形式でデータ送信できるのですが、しかし日本語などの2バイト文字や「&」や「?」を正しく扱えません。
 
それらの文字は encodeURIComponent() 関数などでエンコードができます。
 
 
==== readyState プロパティ ====
readyState プロパティについては、次表のように仕様が決まっています。
 
:{|class="wikitable"
|+ readyState プロパティの意味
! 戻り値 !! 意味
|-
| XMLHttpRequest.UNSENT(数値 0) || オブジェクトが構築された。
|-
| XMLHttpRequest.OPENED(数値 1) || open()メソッドが正常に呼び出されました。この状態では、setRequestHeader()を使ってリクエスト・ヘッダーを設定し、send()メソッドを使ってフェッチを開始することができます。
|-
| XMLHttpRequest.HEADERS_RECEIVED(数値 2) || すべてのリダイレクト(もしあれば)が行われ、応答のすべてのヘッダーが受信されていること。
|-
| XMLHttpRequest.LOADING(数値 3) || レスポンスボディの受信中です。
|-
| XMLHttpRequest.DONE(数値 4) || データの転送が完了した、または転送中に何か問題が発生した(無限リダイレクトなど)。
|}
 
HTTPステータスコードは、いくつもありますが、今回のコードに関係ありそうな主要なものは下記の通りです。下記の表以外にもいくつもありますが、要するに「200」であれば「成功」ですし、「200」以外なら通常は失敗です。
 
また、「XMLHttpRequest.DONE」でデータの転送が完了、あるいは転送中に何か問題が発生し(無限リダイレクトなど)異常終了です。
 
 
なお、普通の通信なら readyState プロパティは XMLHttpRequest.UNSENT → XMLHttpRequest.OPENED → XMLHttpRequest.HEADERS_RECEIVED → XMLHttpRequest.LOADING → XMLHttpRequest.DONE の順番で変化していきます(サンプルコードの表示結果もこのような順番になっています)。
 
==== status プロパティ ====
:{|class="wikitable"
|+ status プロパティの意味
! 戻り値 !! 意味
|-
| 200 || 成功
|-
| 404 || リクエストされたリソースが見つからない (Not Found)<br>URLなどが間違っているのが原因
|-
| 500 || サーバーのエラー。サーバーダウンの場合もこれに該当。
|}
 
上述の readyState プロパティおよび status プロパティの説明を合わせて考えれば、readyState 「4」および status 「200」ならば、とりあえず通信の環境構築には成功しています。
 
==== send()メソッドなど ====
コード末尾のほうにある send() メソッドは、ここでは「通信を開始せよ」程度の意味である。「GET」コマンドでも「POST」コマンドでも、どちらの場合でも、send() メソッドによって通信を開始する。
 
send() メソッドによって
xhr.onreadystatechange = function(){ }
で指定した関数の内部が実行される。なお onreadystatechange プロパティは「オン・レディ・ステイト・チェンジ」である。
 
 
<code>document.write("<br>" + xhr.responseText + "<br>");</code> にある responseText プロパティは、通信で帰って来たテキストを意味するプロパティである。
 
=== 高度なコード ===
;イベントリスナーによるイベントハンドリング:<syntaxhighlight lang="javascript">
const req = new XMLHttpRequest();