aboutsummaryrefslogtreecommitdiff
path: root/files/ja/web/guide/ajax/getting_started/index.html
blob: 755dff542402957a77d1b4f6034cf53095bcdecf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
---
title: 始めましょう
slug: Web/Guide/AJAX/Getting_Started
tags:
  - AJAX
  - API
  - Advanced
  - JavaScript
  - WebMechanics
  - XMLHttpRequest
translation_of: Web/Guide/AJAX/Getting_Started
---
<p class="summary">この記事は AJAX の基礎の概観と、入門のための二つの実践的なサンプルを示します。</p>

<h3 id="Whats_AJAX" name="What's_AJAX">AJAX とは?</h3>

<p>AJAX は <strong>A</strong>synchronous <strong>J</strong>avaScript <strong>A</strong>nd <strong>X</strong>ML の頭文字を取ったものです。これは一言で言えば、 <code><a href="/ja/docs/XMLHttpRequest">XMLHttpRequest</a></code> オブジェクトを使ってサーバーと通信することです。 AJAX は JSON, XML, HTML, テキストファイルなど、様々な形式の情報で送受信することができます。 AJAX の最も魅力的な特徴は「非同期」であること、つまり、サーバーとの通信、データの交換、ページの更新を、ページの再読み込みなしに行うことができる点です。</p>

<p>AJAX でできることには、二つの重要な特徴があります。</p>

<ul>
 <li>ページを再読み込みすることなくサーバーに要求を送る</li>
 <li>サーバーからデータを受け取って処理する</li>
</ul>

<h3 id="Step_1_–_How_to_make_an_HTTP_request" name="Step_1_–_How_to_make_an_HTTP_request">Step 1 –  HTTP リクエストの送り方</h3>

<p>JavaScript からサーバーに <a href="/ja/HTTP">HTTP</a> リクエストを送るためには、この機能を提供するオブジェクトのインスタンスが必要になります。これが <code>XMLHttpRequest</code> の登場する場所です。このクラスは、もともとは Internet Explorer で <code>XMLHTTP</code> と呼ばれる ActiveX オブジェクトとして導入されたものです。その後、 Mozilla や Safari やその他のブラウザがこれに追随し、 Microsoft 独自の ActiveX オブジェクトのメソッドやプロパティに対応する <code>XMLHttpRequest</code> オブジェクトを実装しました。いっぽう、 Microsoft も同様に XMLHttpRequest を実装しました。</p>

<pre class="brush:js notranslate">// 古い互換コードで、もう必要ありません。
if (window.XMLHttpRequest) { // Mozilla, Safari, IE7+ ...
    httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 6 以前
    httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
</pre>

<div class="note"><strong>メモ:</strong> 説明のために、このコードは実際に XMLHttp インスタンスを作成するのに使用するコードよりも多少簡単にしています。より実際に近いサンプルは、この記事の step 3 を見てください。</div>

<p>リクエストを送ったら、応答を受け取った後に何をするかを決めなければなりません。この段階で行う必要があるのは、どの JavaScript 関数に応答を処理させるかを XMLHttp リクエストオブジェクトに教えることだけです。これは、オブジェクトの <code>onreadystatechange</code> プロパティに、使おうとしている JavaScript 関数の名前をこのように設定することで行えます。</p>

<pre class="brush: js notranslate"><code>httpRequest.onreadystatechange = nameOfTheFunction;</code></pre>

<p>このとき、関数名の後に括弧や引数がないことに注意してください。それは、実際にそれを呼ぶのではなく単純に関数の参照を渡しているからです。また、関数名を設定するのではなく、以下のように関数や応答を処理する動作をその場で定義するという JavaScript の機能 (「無名関数」と呼ばれる) を利用することもできます。</p>

<pre class="brush: js notranslate">httpRequest.onreadystatechange = function(){
    // ここでサーバーからの応答を処理します。
};
</pre>

<p>次に、応答を受け取った後に何をするかを宣言したら、以下のように HTTP 要求オブジェクトの <code>open()</code><code>send()</code> 呼び出して、要求を作成する必要があります。</p>

<pre class="brush: js notranslate">httpRequest.open('GET', 'http://www.example.org/some.file', true);
httpRequest.send();
</pre>

<ul>
 <li><code>open()</code> の最初の引数は、サーバーが対応している HTTP リクエストメソッド、つまり、GET、POST、HEAD やその他のメソッドになります。 HTTP 標準に準拠するためにメソッド名はすべて大文字にしてください。そうでなければ、いくつかのブラウザ (Firefox など) ではリクエストを送信しません。利用可能な HTTP リクエストメソッドに関しての詳細情報については <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">W3C の仕様書</a>を参照してください。</li>
 <li>第二引数は、リクエストを送信するページの URL です。セキュリティ上の問題から、他のドメインのページを読むことはできません。つまり、すべてのページに対して同一のドメイン名を利用しないと、 <code>open()</code> を呼び出したときに「権限エラー」を受け取ることになるということです。よくある落とし穴は、サイトに <code>domain.tld</code> でアクセスしながら、<code>www.domain.tld</code> でページを読み込もうとすることです。本当に他のドメインにリクエストを送信する必要がある場合は、 <a href="/ja/docs/Web/HTTP/HTTP_access_control">HTTP アクセス制御</a>を参照してください。</li>
 <li>第三引数は、リクエストを非同期に送るかどうかを示します。 <code>true</code> (既定値) であれば、 JavaScript の実行が継続され、サーバーの応答が届くまでの間もユーザーがページを操作することができます。これが、 AJAX の最初の A です。</li>
</ul>

<p><code>send()</code> メソッドの引数は、要求を <code>POST</code> するときにサーバーに送信したい任意のデータです。フォームデータはサーバーが解釈できる形式、例えばクエリ文字列のような形式、</p>

<pre class="notranslate"><code>"name=value&amp;anothername="+encodeURIComponent(myVar)+"&amp;so=on"</code></pre>

<p>又は、 <code>multipart/form-data</code>, JSON, XML など形式にしてください。</p>

<p>なお、データを <code>POST</code> する場合、要求の MIME タイプを設定する必要がある場合があります。例えば、フォームデータをクエリ文字列として <code>send()</code> を呼び出して送る前に、次の文を使用してください。</p>

<pre class="brush: js notranslate">httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
</pre>

<h3 id="Step_2_–_Handling_the_server_response" name="Step_2_–_Handling_the_server_response">Step 2 – サーバー応答の扱い</h3>

<p>要求を送った時に、応答を扱う JavaScript 関数の名前を設定しました。</p>

<pre class="brush: js notranslate">httpRequest.onreadystatechange = nameOfTheFunction;
</pre>

<p>この関数では何を行うべきでしょうか。最初に、この関数ではリクエストの状態を調べる必要があります。ステータス値が <code>XMLHttpRequest.DONE</code> (4 に対応) であるなら、サーバーからの応答が完了しており、処理を進められることを意味します。</p>

<pre class="brush: js notranslate">if (httpRequest.readyState === XMLHttpRequest.DONE) {
    // 全てが問題ない状態で、応答も返ってきています
} else {
    // まだ準備ができていません
}
</pre>

<p><code>readyState</code> の値のリストは <a href="https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/readyState">XMLHTTPRequest.readyState</a> で文書化されていて、以下のようになっています。</p>

<ul>
 <li>0 (初期化前) 又は (<strong>要求が初期化されていません</strong>)</li>
 <li>1 (読み込み中) 又は (<strong>サーバーへの接続が確立されました</strong>)</li>
 <li>2 (読み込み完了) 又は (<strong>要求を受信しました</strong>)</li>
 <li>3 (対話中) 又は (<strong>要求を処理中です</strong>)</li>
 <li>4 (完了) 又は (<strong>要求が完了して応答の準備ができました</strong>)</li>
</ul>

<p>次に、 HTTP 応答の <a href="/ja/docs/Web/HTTP/#HTTP_Response_Codes">応答コード</a>を調べます。返ってくる可能性があるコードは <a class="external" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">W3C</a> でリスト化されています。以下の例では、 AJAX 呼び出しが成功したか失敗したかを {{HTTPStatus("200", "200 OK")}} 応答コードをチェックすることで判別します。</p>

<pre class="brush: js notranslate">if (httpRequest.status === 200) {
    // 完璧です!
} else {
    // 何らかの問題が発生しています。
    // たとえば、応答に 404 (Not Found) や
    // 500 (Internal Server Error) 応答コードが返っているなど。
}
</pre>

<p>要求の状態と応答の HTTP 状態コードをチェックした後、サーバーが送信したデータを使って好きなことが何でもできます。データにアクセスするには二つの選択肢があります。</p>

<ul>
 <li><code>httpRequest.responseText</code> – サーバーの応答をテキスト文字列として返します</li>
 <li><code>httpRequest.responseXML</code> – サーバーの応答を JavaScript DOM 関数で扱える <code>XMLDocument</code> オブジェクトとして返します</li>
</ul>

<p>なお、上記の段階は非同期要求を使用した場合 (<code>open()</code> の第三引数が未指定か <code>true</code> に設定されていた場合) のみ有効です。<strong>同期</strong>要求を使用した場合は関数を指定する必要はありませんが、これはユーザーの使い勝手をひどく損なうので、避けるべきです。</p>

<h3 id="Step_3_–_A_Simple_Example" name="Step_3_–_A_Simple_Example">Step 3 –  簡単な例</h3>

<p>さて、ここまでに紹介した方法を使って簡単な HTTP リクエストを実行してみましょう。われわれの JavaScript では <code>test.html</code> という名前の、 "これはテストです" と書かれた HTML 文書を要求し、その内容を <code>alert()</code> で表示します。注意として、この例では vanilla JavaScript を使っています — jQuery は入っていません。また HTML, XML, PHP ファイルは同一ディレクトリに置かれています。</p>

<pre class="brush: html notranslate">&lt;button id="ajaxButton" type="button"&gt;要求を実行&lt;/button&gt;

&lt;script&gt;
(function() {
  var httpRequest;
  document.getElementById("ajaxButton").addEventListener('click', makeRequest);

  function makeRequest() {
    httpRequest = new XMLHttpRequest();

    if (!httpRequest) {
      alert('中断 :( XMLHTTP インスタンスを生成できませんでした');
      return false;
    }
    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('GET', 'test.html');
    httpRequest.send();
  }

  function alertContents() {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        alert(httpRequest.responseText);
      } else {
        alert('リクエストに問題が発生しました');
      }
    }
  }
})();
&lt;/script&gt;
</pre>

<p>このサンプルでは以下のことを行います。</p>

<ul>
 <li>ユーザーがブラウザーで「要求を実行」をクリックする。</li>
 <li>イベントハンドラーから <code>makeRequest()</code> 関数が呼び出される。</li>
 <li>要求が作成され、(<code>onreadystatechange</code>) により <code>alertContents()</code> への処理引継ぎが設定される。</li>
 <li><code>alertContents()</code> では、応答が返ってきていて問題無いかを確認した後、<code>test.html</code> ファイルの中身を <code>alert()</code> で表示する。</li>
</ul>

<div class="note"><strong>メモ</strong>: 要求を送信する先が静的な HTML ファイルではなく、 XML を返すコードである場合、 Internet Explorer に応答ヘッダーを設定しなければなりません。<code>Content-Type: application/xml</code> というヘッダーを設定しなければ、XML 要素にアクセスしようとしている行で IE が "Object Expected" という Javascript エラーを投げるでしょう。</div>

<div class="note"><strong>メモ 2</strong>: <code>Cache-Control: no-cache</code> というヘッダーを設定しなければ、ブラウザーが応答をキャッシュして要求を再送信しなくなるため、デバッグが難しくなるでしょう。 GET 引数に、タイムスタンプやランダムな数字のような、常に異なるものを追加する方法もあります (<a href="/ja/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache">キャッシュをバイパスする</a>をご覧ください)</div>

<div class="note"><strong>メモ 3</strong>: <code>httpRequest</code> 変数をグローバルに使用すると、関数の呼び出しが競合して <code>makeRequest()</code> が互いに上書きし合うため、競合状態が発生します。 <code>httpRequest</code> 変数を、 AJAX 関数を含んでいる<a href="/ja/docs/Web/JavaScript/Closures">クロージャ</a>のローカルで宣言することでこれを防ぐことができます。</div>

<p>通信エラーのイベント (サーバーがダウンしたなど) では、応答状態にアクセスする時に <code>onreadystatechange</code> メソッドの中で例外が発生します。この問題を防ぐため、 <code>if...then</code> 文は必ず <code>try...catch</code> で囲むようにしてください。</p>

<pre class="brush: js notranslate">function alertContents() {
  try {
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        alert(httpRequest.responseText);
      } else {
        alert('リクエストに問題が発生しました');
      }
    }
  }
  catch( e ) {
    alert('例外を捕捉: ' + e.description);
  }
}
</pre>

<h3 id="Step_4_–_Working_with_the_XML_response" name="Step_4_–_Working_with_the_XML_response">Step 4 – 「X-ファイル」 もしくは XML レスポンスの扱い方</h3>

<p>前の例では、 HTTP リクエストへの応答を受け取った後、要求オブジェクトの <code>responseText</code> プロパティを用いて、それに含まれている test.html の中身を取得しました。では、次に <code>responseXML</code> プロパティのほうを試してみましょう。</p>

<p>はじめに、あとでサーバーに要求する妥当な XML 文書を作成します。 test.xml ファイルの中身は以下のようなものです。</p>

<pre class="brush: html notranslate">&lt;?xml version="1.0" ?&gt;
&lt;root&gt;
    I'm a test.
&lt;/root&gt;
</pre>

<p>スクリプトでは、リクエスト送出を以下のように変更します。</p>

<pre class="brush: html notranslate">...
onclick="makeRequest('test.xml')"&gt;
...
</pre>

<p>そして、 <code>alertContents()</code> では、 <code>alert(httpRequest.responseText);</code> としている行を以下のように変更します。</p>

<pre class="brush: js notranslate">var xmldoc = httpRequest.responseXML;
var root_node = xmldoc.getElementsByTagName('root').item(0);
alert(root_node.firstChild.data);
</pre>

<p>このコードでは、 <code>responseXML</code> から <code>XMLDocument</code> オブジェクトを取得し、 DOM メソッドを利用して XML 文書に含まれるデータにアクセスしています。 この<code>test.xml</code> ファイルは<a href="http://www.w3clubs.com/mozdev/test.xml">ここ</a>で、変更されたスクリプトは<a href="http://www.w3clubs.com/mozdev/httprequest_test_xml.html">ここ</a>で見ることができます。</p>

<h3 id="Step_5_–_Working_with_data" name="Step_5_–_Working_with_data">Step 5 – データを処理する</h3>

<p>最後に、データをサーバーに送って応答を受けましょう。 JavaScript はここで動的なページ <code>test.php</code> に要求し、このページは送ったデータを受けて「計算した」文字 - "Hello, [user data]!" - を返し、これを <code>alert()</code> します。</p>

<p>まずは HTML にテキストボックスを追加してユーザーが名前を入れられるようにします:</p>

<pre class="brush: html notranslate">&lt;label&gt;Your name:
  &lt;input type="text" id="ajaxTextbox" /&gt;
&lt;/label&gt;
&lt;span id="ajaxButton" style="cursor: pointer; text-decoration: underline"&gt;
  Make a request
&lt;/span&gt;</pre>

<p>イベントハンドラーに、テキストボックスからユーザーデータを取得してサーバーサイドスクリプトの URL と一緒に <code>makeRequest()</code> に送るような行も追加します。</p>

<pre class="brush: js notranslate">  document.getElementById("ajaxButton").onclick = function() {
      var userName = document.getElementById("ajaxTextbox").value;
      makeRequest('test.php',userName);
  };
</pre>

<p><code>makeRequest()</code> を編集してユーザーデータを受け取ってサーバーに渡すようにします。リクエストメソッドは <code>GET</code> から <code>POST</code> に変更し、データを <code>httpRequest.send()</code> 呼び出しのパラメーターとして入れます:</p>

<pre class="brush: js notranslate">  function makeRequest(url, userName) {

    ...

    httpRequest.onreadystatechange = alertContents;
    httpRequest.open('POST', url);
    httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    httpRequest.send('userName=' + encodeURIComponent(userName));
  }
</pre>

<p><code>alertContents()</code> 関数はステップ 3 と同じように書かれて、サーバーが計算された文字列を返していたら、 alert するようにします。しかし、サーバーが計算された文字列とオリジナルのユーザーデータの両方を返していたらどうでしょう?ユーザーがテキストボックスに "Jane" とタイプしていたら、サーバーの応答はこのようになります:</p>

<p><code>{"userData":"Jane","computedString":"Hi, Jane!"}</code></p>

<p>このデータを <code>alertContents()</code>,内で使うには、単に <code>responseText</code> をalert することはできず、これを parse して、求めるプロパティの <code>computedString</code> をalertします:</p>

<pre class="brush: js notranslate">function alertContents() {
  if (httpRequest.readyState === XMLHttpRequest.DONE) {
    if (httpRequest.status === 200) {
      var response = JSON.parse(httpRequest.responseText);
      alert(response.computedString);
    } else {
      alert('There was a problem with the request.');
    }
  }
}</pre>

<p><code>test.php</code> には以下のようなものが入ります。</p>

<pre class="brush: php notranslate"><code>$name = (isset($_POST['userName'])) ? $_POST['userName'] : 'no name';
$computedString = "Hi, " . $name;
$array = ['userName' =&gt; $name, 'computedString' =&gt; $computedString];
echo json_encode($array);</code></pre>

<p>DOM メソッドについてより詳しくは、<a href="/ja/docs/dom/">Mozilla での DOM の実装</a>の文書を参照してください。</p>