aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/api/xmlhttprequest/using_xmlhttprequest/index.html
blob: 4304530ac741853c06ccfec30e3cb7e7901fe073 (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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
---
title: Использование XMLHttpRequest
slug: Web/API/XMLHttpRequest/Using_XMLHttpRequest
translation_of: Web/API/XMLHttpRequest/Using_XMLHttpRequest
---
<p>Это инструкция по использованию <code><a href="/en-US/docs/DOM/XMLHttpRequest" title="XMLHttpRequest">XMLHttpRequest</a></code>, для взаимодействия через HTTP-протокол.</p>

<p>Для отправки HTTP-запроса нужно создать XMLHttpRequest-объект, открыть URL и отправить запрос. После выполнения запроса можно получить и обработать тело и статус ответа.</p>

<pre class="brush: js notranslate">function reqListener () {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.open("get", "yourFile.txt", true);
oReq.send();</pre>

<h2 id="Типы_запросов">Типы запросов</h2>

<p>Запрос, сделанный посредством <code>XMLHttpRequest</code>, может загружать данные синхронно или асинхронно. Это определяется третьим аргументом метода <a href="/en-US/docs/DOM/XMLHttpRequest#open()" title="DOM/XMLHttpRequest#open()">open()</a>  объекта XMLHttpRequest: если он равен true или не определён, запрос выполнится асинхронно, в противном случае — синхронно. Детальное обсуждение и демонстрации обоих типов запросов могут быть найдены на странице <a href="/en-US/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests" title="Synchronous and Asynchronous Requests">synchronous and asynchronous requests</a>. Использовать синхронные запросы приходится очень редко.</p>

<div class="note"><strong>Примечание:</strong> Начиная с Gecko 30.0 {{ geckoRelease("30.0") }} не рекомендуется использовать синхронные запросы, так как они отрицательно влияют на пользовательский опыт.</div>

<h2 id="Обработка_запросов">Обработка запросов</h2>

<p>Есть несколько типов <a href="http://www.w3.org/TR/XMLHttpRequest2/#response">атрибутов ответа</a>, определённых спецификацией W3C XMLHttpRequest. Они сообщают запрашивающему важную информацию о статусе ответа. В некоторых случаях обработка нетекстовых типов ответа может потребовать дополнительных действий и анализа; эти случаи описаны ниже.</p>

<h3 id="Анализ_и_использование_свойства_responseXML">Анализ и использование свойства <code>responseXML</code></h3>

<p>Если скачать XML документ с помощью <code>XMLHttpRequest</code>, в свойстве <code>responseXML</code> будет объект DOM, содержащим распарсенный XML документ. Напрямую работать с ним будет сложно. Есть четыре основных способа анализа этого документа:</p>

<ol>
 <li>Использовать <a href="/ru/docs/XPath" title="XPath">XPath</a>, чтобы указывать на его части.</li>
 <li>Использовать <a href="/ru/docs/JXON" title="JXON">JSON</a>, чтобы превратить его в дерево объектов JavaScript.</li>
 <li>Вручную <a href="/ru/docs/Parsing_and_serializing_XML">парсить и превращать XML</a> в строки или объекты. </li>
 <li>Использовать <a href="/ru/docs/XMLSerializer" title="XMLSerializer">XMLSerializer</a>, чтобы превращать деревья DOM в строки или файлы.</li>
 <li>Можно использовать <a href="/ru/docs/JavaScript/Reference/Global_Objects/RegExp">регулярные выражения</a>, если вы заранее знаете содержимое документа. Возможно, стоит удалить переносы строк, если вы используете регулярные выражения с оглядкой на переносы. Однако этот метод стоит использовать только в крайнем случае, ведь если XML изменится, хотя бы чуть-чуть, то регулярное выражение, скорее всего, не справится.</li>
</ol>

<h3 id="Анализ_и_использование_свойства_responseText_содержащего_HTML_документ">Анализ и использование свойства <code>responseText,</code> содержащего <code>HTML</code> документ</h3>

<div class="note"><strong>Примечание:</strong> Спецификация W3C <a href="http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html">XMLHttpRequest</a> позволяет парсить HTML через свойство <code>XMLHttpRequest.responseXML</code>. Подробнее об этом написано в статье <a href="/ru/docs/HTML_in_XMLHttpRequest" title="HTML_in_XMLHttpRequest">HTML в XMLHttpRequest</a>.</div>

<p>Если получить содержимое HTML страницы с помощью <code>XMLHttpRequest</code>, свойство <code>responseText</code> будет строкой, содержащей "кашу" изо всех HTML тэгов, с которой будет очень сложно работать. Есть три основных способа анализа этой HTML строки:</p>

<ol>
 <li>Использовать свойство <code>XMLHttpRequest.responseXML<strong>.</strong></code></li>
 <li>Вставить содержимое в тело <a href="/ru/docs/Web/API/DocumentFragment">фрагмента документа</a> с помощью <code>fragment.body.innerHTML</code> и работать уже с этим фрагментом.</li>
 <li>Можно использовать <a href="/ru/docs/JavaScript/Reference/Global_Objects/RegExp">регулярные выражения</a>, если вы заранее знаете содержимое документа.</li>
</ol>

<h2 id="Работа_с_двоичными_данными">Работа с двоичными данными</h2>

<p>Хотя обычно <code>XMLHttpRequest</code> используется, чтобы получать и загружать текст, с его помощью можно обмениваться и двоичными данными. Есть несколько проверенных способов заставить <code>XMLHttpRequest</code> посылать двоичные данные. Они используют метод  <code>XMLHttpRequest</code>.overrideMimeType().</p>

<pre class="brush:js notranslate">var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
// получаем необработанные данные в виде двоичной строки
oReq.overrideMimeType("text/plain; charset=x-user-defined");
/* ... */
</pre>

<p>Спецификация XMLHttpRequest Level 2 добавляет новые атрибуты <a href="http://www.w3.org/TR/XMLHttpRequest2/#the-responsetype-attribute" title="http://www.w3.org/TR/XMLHttpRequest2/#the-responsetype-attribute">responseType</a>, значительно облегчающие работу с двоичными данными:</p>

<pre class="brush:js notranslate">var oReq = new XMLHttpRequest();

oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
  var arraybuffer = oReq.response; // not responseText
  /* ... */
}
oReq.send();
</pre>

<p>Больше примеров можно найти на странице <a href="/ru/docs/DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data" title="DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data">Отправка и получение двоичных данных</a>.</p>

<h2 id="Отслеживание_процесса_загрузки">Отслеживание процесса загрузки</h2>

<p><code>XMLHttpRequest</code> позволяет подписываться на различные события, которые могут произойти в процессе обработки запроса. Сюда входят периодические уведомления о состоянии запроса, сообщения об ошибках и так далее. </p>

<p>Отслеживание событий процесса загрузки  следует спецификации Web API <a href="http://dev.w3.org/2006/webapi/progress/Progress.html" title="http://dev.w3.org/2006/webapi/progress/Progress.html">progress events</a>: эти события реализуют интерфейс {{domxref("ProgressEvent")}}.</p>

<pre class="brush:js notranslate">var oReq = new XMLHttpRequest();

oReq.addEventListener("progress", updateProgress, false);
oReq.addEventListener("load", transferComplete, false);
oReq.addEventListener("error", transferFailed, false);
oReq.addEventListener("abort", transferCanceled, false);

oReq.open();

// ...

// состояние передачи от сервера к клиенту (загрузка)
function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total;
    // ...
  } else {
    // Невозможно вычислить состояние загрузки, так как размер неизвестен
  }
}

function transferComplete(evt) {
  alert("Загрузка завершена.");
}

function transferFailed(evt) {
  alert("При загрузке файла произошла ошибка.");
}

function transferCanceled(evt) {
  alert("Пользователь отменил загрузку.");
}</pre>

<p>На строчках 3-6 добавляются обработчики для различных событий, происходящих при передаче данных с помощью <code>XMLHttpRequest</code>.</p>

<div class="note"><strong>Примечание:</strong> Обработчики нужно добавлять до того, как вызвать <code>open(). В противном случае события не будут обработаны.</code></div>

<p>Обработчик события <code>progress</code>, представленный функцией <code>updateProgress()</code> в этом примере, получает количество байт, которое должно быть передано, и количество уже переданных байт в полях <code>total</code> и <code>loaded.</code> Но если поле <code>lengthComputable</code> равняется <code>false</code>, значит, длина сообщения неизвестна и будет отображаться как ноль.</p>

<p>События <code>progress</code> есть и у входящих, и у исходящих передач. События входящих передач создаются для объекта <code>XMLHttpRequest</code>, как показано в примере выше; исходящих —для <code>XMLHttpRequest.upload:</code></p>

<pre class="brush:js notranslate">var oReq = new XMLHttpRequest();

oReq.upload.addEventListener("progress", updateProgress, false);
oReq.upload.addEventListener("load", transferComplete, false);
oReq.upload.addEventListener("error", transferFailed, false);
oReq.upload.addEventListener("abort", transferCanceled, false);

oReq.open();
</pre>

<div class="note"><strong>Примечание:</strong> события progress недоступны для протокола <code>file: .</code></div>

<div class="note"><strong>Примечание</strong>: На данный момент в событиях progress есть открытые ошибки, которые влияют на Firefox 25 для <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=908375">OS X</a> и <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=786953">Linux</a>.</div>

<div class="note">
<p><strong>Примечание:</strong> Начиная с {{Gecko("9.0")}}, можно быть уверенным, что события progress будут приходить для каждого пакета данных, включая последний пакет в случаях, когда он получен, и соединение закрыто прежде, чем будет создано событие progress. В этом случае, событие progress автоматическисоздастся, когда будет получено событие load для этого пакета. Это позволяет следить за процессом загрузки с помощью только событий progress.</p>
</div>

<div class="note">
<p><strong>Примечание:</strong> В {{Gecko("12.0")}}, если событие progress вызвано с  <code>responseType</code> "moz-blob", значение ответа будет {{domxref("Blob")}}, содержащим все данные, полученные на текущий момент.</p>
</div>

<p>Также возможно засечь все три события, завершающие загрузку (<code>abort</code>, <code>load</code>, or <code>error</code>) через событие <code>loadend</code>:</p>

<pre class="brush:js notranslate">req.addEventListener("loadend", loadEnd, false);

function loadEnd(e) {
  alert("Передача данных завершена (но мы не знаем, успешно ли).");
}
</pre>

<p>Заметьте, что событие loadend никак не сообщает, что вызвало конец передачи. Впрочем, это никак не мешает использовать его, если нужно сделать что-то вне зависимости от причины.</p>

<h2 id="Отправка_форм_и_загрузка_файлов">Отправка форм и загрузка файлов</h2>

<p>Есть два способа передать данные форм с помощью экземпляра <code>XMLHttpRequest</code>:</p>

<ul>
 <li>используя только AJAX</li>
 <li>используя API <a href="/en-US/docs/DOM/XMLHttpRequest/FormData" title="DOM/XMLHttpRequest/FormData"><code>FormData</code></a></li>
</ul>

<p><strong>Второй путь</strong> — самый простой и быстрый, но данные, полученные с его помощью, нельзя превратить в строки с помощью <a href="/ru/docs/JavaScript/Reference/Global_Objects/JSON/stringify">JSON.stringify</a>. <strong>Первый путь</strong>, наоборот, самый сложный, но зато самый гибкий и мощный.</p>

<h3 id="Используя_только_XMLHttpRequest">Используя только <code>XMLHttpRequest</code></h3>

<p>Отправка форм без <a href="/en-US/docs/DOM/XMLHttpRequest/FormData" title="DOM/XMLHttpRequest/FormData"><code>FormData</code></a> не требует других API, кроме <code><a href="/en-US/docs/DOM/FileReader" title="/en-US/docs/DOM/FileReader">FileReader</a> </code>для загрузки файлов.</p>

<h4 id="Краткое_введение_в_методы_отправки">Краткое введение в методы отправки</h4>

<p>Есть четыре способа послать HTML {{ HTMLElement("form") }}:</p>

<ul>
 <li>использовать метод <code>POST</code> и установить атрибут <code>enctype</code> =  <code>application/x-www-form-urlencoded</code> (он установлен по умолчанию);</li>
 <li>использовать метод <code>POST</code> и установить атрибут <code>enctype</code> = <code>text/plain</code>;</li>
 <li>использовать метод <code>POST</code> и установить атрибут <code>enctype</code> = <code>multipart/form-data</code>;</li>
 <li>использовать метод <code>GET </code>(в этом случае атрибут <code>enctype</code> будет проигнорирован).</li>
</ul>

<p>Рассмотрим отправку формы с двумя полями, <code>foo</code> и <code>baz</code>. Если использовать метод <code>POST</code>, сервер получит строку, похожую на одну из показанных ниже, в зависимости от типа кодирования, который вы используете:</p>

<ul>
 <li>
  <p>Метод: <code>POST</code>; тип кодирования: <code>application/x-www-form-urlencoded</code> (по умолчанию):</p>

  <pre class="notranslate">Content-Type: application/x-www-form-urlencoded

foo=bar&amp;baz=The+first+line.&amp;#37;0D%0AThe+second+line.%0D%0A</pre>
 </li>
 <li>
  <p>Метод: <code>POST</code>; тип кодирования: <code>text/plain</code>:</p>

  <pre class="notranslate">Content-Type: text/plain

foo=bar
baz=The first line.
The second line.</pre>
 </li>
 <li>
  <p>Метод: <code>POST</code>; тип кодирования: <code>multipart/form-data</code>:</p>

  <pre style="height: 100px; overflow: auto;">Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--</pre>
 </li>
</ul>

<p>Instead, if you are using the <code>GET</code> method, a string like the following will be simply added to the URL:</p>

<pre class="notranslate">?foo=bar&amp;baz=The%20first%20line.%0AThe%20second%20line.</pre>

<h4 id="Небольшой_классический_фреймворк">Небольшой классический фреймворк</h4>

<p>Все данные эффекты достигаются с помощью веб браузера, когда вы отправляете {{ HTMLElement("form") }}. Но если вам требуется выполнить все операции с помощью лишь JavaScript, то вам придется проинструктировать интерпретатор обо <em>всех</em> выполняемых операциях. Для, того чтобы отправлять формы с помощью <em>чистого</em> AJAX, потребуется слишком комплексное описание, чтобы тут рассуждать о нем во всех подробностях. В связи с этим, мы опубликовали здесь <strong>полный(но все еще дидактический) фреймворк, </strong>который все же способен использовать все четыре способа отправки и, так же поддерживает <strong>файловую загрузку.</strong></p>

<div style="height: 400px; margin-bottom: 12px; overflow: auto;">
<pre class="brush: html notranslate">&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;title&gt;Sending forms with pure AJAX &amp;ndash; MDN&lt;/title&gt;
&lt;script type="text/javascript"&gt;

"use strict";

/*\
|*|
|*|  :: XMLHttpRequest.prototype.sendAsBinary() Polyfill ::
|*|
|*|  https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()
\*/

if (!XMLHttpRequest.prototype.sendAsBinary) {
  XMLHttpRequest.prototype.sendAsBinary = function(sData) {
    var nBytes = sData.length, ui8Data = new Uint8Array(nBytes);
    for (var nIdx = 0; nIdx &lt; nBytes; nIdx++) {
      ui8Data[nIdx] = sData.charCodeAt(nIdx) &amp; 0xff;
    }
    /* send as ArrayBufferView...: */
    this.send(ui8Data);
    /* ...or as ArrayBuffer (legacy)...: this.send(ui8Data.buffer); */
  };
}

/*\
|*|
|*|  :: AJAX Form Submit Framework ::
|*|
|*|  https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest
|*|
|*|  This framework is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*|  Syntax:
|*|
|*|   AJAXSubmit(HTMLFormElement);
\*/

var AJAXSubmit = (function () {

  function ajaxSuccess () {
    /* console.log("AJAXSubmit - Success!"); */
    alert(this.responseText);
    /* you can get the serialized data through the "submittedData" custom property: */
    /* alert(JSON.stringify(this.submittedData)); */
  }

  function submitData (oData) {
    /* the AJAX request... */
    var oAjaxReq = new XMLHttpRequest();
    oAjaxReq.submittedData = oData;
    oAjaxReq.onload = ajaxSuccess;
    if (oData.technique === 0) {
      /* method is GET */
      oAjaxReq.open("get", oData.receiver.replace(/(?:\?.*)?$/, oData.segments.length &gt; 0 ? "?" + oData.segments.join("&amp;") : ""), true);
      oAjaxReq.send(null);
    } else {
      /* method is POST */
      oAjaxReq.open("post", oData.receiver, true);
      if (oData.technique === 3) {
        /* enctype is multipart/form-data */
        var sBoundary = "---------------------------" + Date.now().toString(16);
        oAjaxReq.setRequestHeader("Content-Type", "multipart\/form-data; boundary=" + sBoundary);
        oAjaxReq.sendAsBinary("--" + sBoundary + "\r\n" + oData.segments.join("--" + sBoundary + "\r\n") + "--" + sBoundary + "--\r\n");
      } else {
        /* enctype is application/x-www-form-urlencoded or text/plain */
        oAjaxReq.setRequestHeader("Content-Type", oData.contentType);
        oAjaxReq.send(oData.segments.join(oData.technique === 2 ? "\r\n" : "&amp;"));
      }
    }
  }

  function processStatus (oData) {
    if (oData.status &gt; 0) { return; }
    /* the form is now totally serialized! do something before sending it to the server... */
    /* doSomething(oData); */
    /* console.log("AJAXSubmit - The form is now serialized. Submitting..."); */
    submitData (oData);
  }

  function pushSegment (oFREvt) {
    this.owner.segments[this.segmentIdx] += oFREvt.target.result + "\r\n";
    this.owner.status--;
    processStatus(this.owner);
  }

  function plainEscape (sText) {
    /* how should I treat a text/plain form encoding? what characters are not allowed? this is what I suppose...: */
    /* "4\3\7 - Einstein said E=mc2" ----&gt; "4\\3\\7\ -\ Einstein\ said\ E\=mc2" */
    return sText.replace(/[\s\=\\]/g, "\\$&amp;");
  }

  function SubmitRequest (oTarget) {
    var nFile, sFieldType, oField, oSegmReq, oFile, bIsPost = oTarget.method.toLowerCase() === "post";
    /* console.log("AJAXSubmit - Serializing form..."); */
    this.contentType = bIsPost &amp;&amp; oTarget.enctype ? oTarget.enctype : "application\/x-www-form-urlencoded";
    this.technique = bIsPost ? this.contentType === "multipart\/form-data" ? 3 : this.contentType === "text\/plain" ? 2 : 1 : 0;
    this.receiver = oTarget.action;
    this.status = 0;
    this.segments = [];
    var fFilter = this.technique === 2 ? plainEscape : escape;
    for (var nItem = 0; nItem &lt; oTarget.elements.length; nItem++) {
      oField = oTarget.elements[nItem];
      if (!oField.hasAttribute("name")) { continue; }
      sFieldType = oField.nodeName.toUpperCase() === "INPUT" ? oField.getAttribute("type").toUpperCase() : "TEXT";
      if (sFieldType === "FILE" &amp;&amp; oField.files.length &gt; 0) {
        if (this.technique === 3) {
          /* enctype is multipart/form-data */
          for (nFile = 0; nFile &lt; oField.files.length; nFile++) {
            oFile = oField.files[nFile];
            oSegmReq = new FileReader();
            /* (custom properties:) */
            oSegmReq.segmentIdx = this.segments.length;
            oSegmReq.owner = this;
            /* (end of custom properties) */
            oSegmReq.onload = pushSegment;
            this.segments.push("Content-Disposition: form-data; name=\"" + oField.name + "\"; filename=\""+ oFile.name + "\"\r\nContent-Type: " + oFile.type + "\r\n\r\n");
            this.status++;
            oSegmReq.readAsBinaryString(oFile);
          }
        } else {
          /* enctype is application/x-www-form-urlencoded or text/plain or method is GET: files will not be sent! */
          for (nFile = 0; nFile &lt; oField.files.length; this.segments.push(fFilter(oField.name) + "=" + fFilter(oField.files[nFile++].name)));
        }
      } else if ((sFieldType !== "RADIO" &amp;&amp; sFieldType !== "CHECKBOX") || oField.checked) {
        /* field type is not FILE or is FILE but is empty */
        this.segments.push(
          this.technique === 3 ? /* enctype is multipart/form-data */
            "Content-Disposition: form-data; name=\"" + oField.name + "\"\r\n\r\n" + oField.value + "\r\n"
          : /* enctype is application/x-www-form-urlencoded or text/plain or method is GET */
            fFilter(oField.name) + "=" + fFilter(oField.value)
        );
      }
    }
    processStatus(this);
  }

  return function (oFormElement) {
    if (!oFormElement.action) { return; }
    new SubmitRequest(oFormElement);
  };

})();

&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h1&gt;Sending forms with pure AJAX&lt;/h1&gt;

&lt;h2&gt;Using the GET method&lt;/h2&gt;

&lt;form action="register.php" method="get" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Registration example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;h2&gt;Using the POST method&lt;/h2&gt;
&lt;h3&gt;Enctype: application/x-www-form-urlencoded (default)&lt;/h3&gt;

&lt;form action="register.php" method="post" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Registration example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;h3&gt;Enctype: text/plain&lt;/h3&gt;

&lt;form action="register.php" method="post" enctype="text/plain" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Registration example&lt;/legend&gt;
    &lt;p&gt;
      Your name: &lt;input type="text" name="user" /&gt;
    &lt;/p&gt;
    &lt;p&gt;
      Your message:&lt;br /&gt;
      &lt;textarea name="message" cols="40" rows="8"&gt;&lt;/textarea&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;h3&gt;Enctype: multipart/form-data&lt;/h3&gt;

&lt;form action="register.php" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Upload example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;&lt;br /&gt;
      Sex:
      &lt;input id="sex_male" type="radio" name="sex" value="male" /&gt; &lt;label for="sex_male"&gt;Male&lt;/label&gt;
      &lt;input id="sex_female" type="radio" name="sex" value="female" /&gt; &lt;label for="sex_female"&gt;Female&lt;/label&gt;&lt;br /&gt;
      Password: &lt;input type="password" name="secret" /&gt;&lt;br /&gt;
      What do you prefer:
      &lt;select name="image_type"&gt;
        &lt;option&gt;Books&lt;/option&gt;
        &lt;option&gt;Cinema&lt;/option&gt;
        &lt;option&gt;TV&lt;/option&gt;
      &lt;/select&gt;
    &lt;/p&gt;
    &lt;p&gt;
      Post your photos:
      &lt;input type="file" multiple name="photos[]"&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input id="vehicle_bike" type="checkbox" name="vehicle[]" value="Bike" /&gt; &lt;label for="vehicle_bike"&gt;I have a bike&lt;/label&gt;&lt;br /&gt;
      &lt;input id="vehicle_car" type="checkbox" name="vehicle[]" value="Car" /&gt; &lt;label for="vehicle_car"&gt;I have a car&lt;/label&gt;
    &lt;/p&gt;
    &lt;p&gt;
      Describe yourself:&lt;br /&gt;
      &lt;textarea name="description" cols="50" rows="8"&gt;&lt;/textarea&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
</div>

<p>Для того, чтобы произвести его тест, создайте страницу с названием <strong>register.php</strong> (и укажите атрибут <code>action</code> одной из данных форм) и добавьте данный <em>минимальный</em> контент</p>

<pre class="brush: php notranslate">&lt;?php
/* register.php */

header("Content-type: text/plain");

/*
NOTE: You should never use `print_r()` in production scripts, or
otherwise output client-submitted data without sanitizing it first.
Failing to sanitize can lead to cross-site scripting vulnerabilities.
*/

echo ":: data received via GET ::\n\n";
print_r($_GET);

echo "\n\n:: Data received via POST ::\n\n";
print_r($_POST);

echo "\n\n:: Data received as \"raw\" (text/plain encoding) ::\n\n";
if (isset($HTTP_RAW_POST_DATA)) { echo $HTTP_RAW_POST_DATA; }

echo "\n\n:: Files received ::\n\n";
print_r($_FILES);

</pre>

<p>Синтаксис данного скрипта представлен ниже:</p>

<pre class="syntaxbox notranslate">AJAXSubmit(myForm);</pre>

<div class="note"><strong>Заметка:</strong> Данный фреймворк использует <a href="/en-US/docs/DOM/FileReader" title="/en-US/docs/DOM/FileReader"><code>FileReader</code></a> API для передачи загрузочных файлов. Это новый API и его невозможно использовать IE9 и ниже. В связи с этим, загрузки только с использованием AJAX воспринимаются, лишь как <strong>экспериментальные</strong>. Если вам не требуется загружать бинарные файлы, то данный фреймворк работает в большинстве современных браузерах.</div>

<div class="note"><strong>Note:</strong> The best way to send binary content is via <a href="/en-US/docs/JavaScript/Typed_arrays/ArrayBuffer" title="/en-US/docs/JavaScript/Typed_arrays/ArrayBuffer">ArrayBuffers</a> or <a href="/en-US/docs/DOM/Blob" title="/en-US/docs/DOM/Blob">Blobs</a> in conjuncton with the <a href="/en-US/docs/DOM/XMLHttpRequest#send%28%29" title="/en-US/docs/DOM/XMLHttpRequest#send()"><code>send()</code></a> method and possibly the <a href="/en-US/docs/DOM/FileReader#readAsArrayBuffer()" title="/en-US/docs/DOM/FileReader#readAsArrayBuffer()"><code>readAsArrayBuffer()</code></a> method of the <a href="/en-US/docs/DOM/FileReader" title="/en-US/docs/DOM/FileReader"><code>FileReader</code></a> API. But, since the aim of this script is to work with a <a href="/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify" title="/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify">stringifiable</a> raw data, we used the <a href="/en-US/docs/DOM/XMLHttpRequest#sendAsBinary%28%29" title="/en-US/docs/DOM/XMLHttpRequest#sendAsBinary()"><code>sendAsBinary()</code></a> method in conjunction with the <a href="/en-US/docs/DOM/FileReader#readAsBinaryString%28%29" title="/en-US/docs/DOM/FileReader#readAsBinaryString()"><code>readAsBinaryString()</code></a> method of the <a href="/en-US/docs/DOM/FileReader" title="/en-US/docs/DOM/FileReader"><code>FileReader</code></a> API. As such, the above script makes sense only when you are dealing with small files. If you do not intend to upload binary content, consider instead using the <a href="/en-US/docs/DOM/XMLHttpRequest/FormData" title="DOM/XMLHttpRequest/FormData"><code>FormData</code></a> API.</div>

<div class="note"><strong>Note:</strong> The non-standard <code>sendAsBinary </code>method is considered deprecated as of Gecko 31 {{ geckoRelease(31) }} and will be removed soon. The standard <code>send(Blob data)</code> method can be used instead.</div>

<h3 id="Используя_объекты_FormData">Используя объекты FormData</h3>

<p>The <a href="/en-US/docs/DOM/XMLHttpRequest/FormData" title="DOM/XMLHttpRequest/FormData"><code>FormData</code></a> constructor lets you compile a set of key/value pairs to send using <code>XMLHttpRequest</code>. Its primarily intended for use in sending form data, but can be used independently from forms in order to transmit keyed data. The transmitted data is in the same format that the form's <code>submit()</code> method would use to send the data if the form's encoding type were set to "multipart/form-data". FormData objects can be utilized in a number of ways with an XMLHttpRequest. For examples and explanations of how one can utilize FormData with XMLHttpRequests see the <a href="/en-US/docs/DOM/XMLHttpRequest/FormData/Using_FormData_Objects" title="Using FormData Objects">Using FormData Objects</a> page. For didactic purpose only we post here <strong>a <em>translation</em> of <a href="#A_little_vanilla_framework" title="#A_little_vanilla_framework">the previous example</a> transformed so as to make use of the <code>FormData</code> API</strong>. Note the brevity of the code:</p>

<div style="height: 400px; margin-bottom: 12px; overflow: auto;">
<pre class="brush: html notranslate">&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
&lt;title&gt;Sending forms with FormData &amp;ndash; MDN&lt;/title&gt;
&lt;script type="text/javascript"&gt;
"use strict";

function ajaxSuccess () {
  alert(this.responseText);
}

function AJAXSubmit (oFormElement) {
  if (!oFormElement.action) { return; }
  var oReq = new XMLHttpRequest();
  oReq.onload = ajaxSuccess;
  if (oFormElement.method.toLowerCase() === "post") {
    oReq.open("post", oFormElement.action, true);
    oReq.send(new FormData(oFormElement));
  } else {
    var oField, sFieldType, nFile, sSearch = "";
    for (var nItem = 0; nItem &lt; oFormElement.elements.length; nItem++) {
      oField = oFormElement.elements[nItem];
      if (!oField.hasAttribute("name")) { continue; }
      sFieldType = oField.nodeName.toUpperCase() === "INPUT" ? oField.getAttribute("type").toUpperCase() : "TEXT";
      if (sFieldType === "FILE") {
        for (nFile = 0; nFile &lt; oField.files.length; sSearch += "&amp;" + escape(oField.name) + "=" + escape(oField.files[nFile++].name));
      } else if ((sFieldType !== "RADIO" &amp;&amp; sFieldType !== "CHECKBOX") || oField.checked) {
        sSearch += "&amp;" + escape(oField.name) + "=" + escape(oField.value);
      }
    }
    oReq.open("get", oFormElement.action.replace(/(?:\?.*)?$/, sSearch.replace(/^&amp;/, "?")), true);
    oReq.send(null);
  }
}
&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h1&gt;Sending forms with FormData&lt;/h1&gt;

&lt;h2&gt;Using the GET method&lt;/h2&gt;

&lt;form action="register.php" method="get" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Registration example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;h2&gt;Using the POST method&lt;/h2&gt;
&lt;h3&gt;Enctype: application/x-www-form-urlencoded (default)&lt;/h3&gt;

&lt;form action="register.php" method="post" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Registration example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;h3&gt;Enctype: text/plain&lt;/h3&gt;

&lt;p&gt;The text/plain encoding is not supported by the FormData API.&lt;/p&gt;

&lt;h3&gt;Enctype: multipart/form-data&lt;/h3&gt;

&lt;form action="register.php" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;"&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Upload example&lt;/legend&gt;
    &lt;p&gt;
      First name: &lt;input type="text" name="firstname" /&gt;&lt;br /&gt;
      Last name: &lt;input type="text" name="lastname" /&gt;&lt;br /&gt;
      Sex:
      &lt;input id="sex_male" type="radio" name="sex" value="male" /&gt; &lt;label for="sex_male"&gt;Male&lt;/label&gt;
      &lt;input id="sex_female" type="radio" name="sex" value="female" /&gt; &lt;label for="sex_female"&gt;Female&lt;/label&gt;&lt;br /&gt;
      Password: &lt;input type="password" name="secret" /&gt;&lt;br /&gt;
      What do you prefer:
      &lt;select name="image_type"&gt;
        &lt;option&gt;Books&lt;/option&gt;
        &lt;option&gt;Cinema&lt;/option&gt;
        &lt;option&gt;TV&lt;/option&gt;
      &lt;/select&gt;
    &lt;/p&gt;
    &lt;p&gt;
      Post your photos:
      &lt;input type="file" multiple name="photos[]"&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input id="vehicle_bike" type="checkbox" name="vehicle[]" value="Bike" /&gt; &lt;label for="vehicle_bike"&gt;I have a bike&lt;/label&gt;&lt;br /&gt;
      &lt;input id="vehicle_car" type="checkbox" name="vehicle[]" value="Car" /&gt; &lt;label for="vehicle_car"&gt;I have a car&lt;/label&gt;
    &lt;/p&gt;
    &lt;p&gt;
      Describe yourself:&lt;br /&gt;
      &lt;textarea name="description" cols="50" rows="8"&gt;&lt;/textarea&gt;
    &lt;/p&gt;
    &lt;p&gt;
      &lt;input type="submit" value="Submit" /&gt;
    &lt;/p&gt;
  &lt;/fieldset&gt;
&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;</pre>
</div>

<div class="note"><strong>Note:</strong> As we said,<strong> {{domxref("FormData")}} objects are not <a href="/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify" title="/en-US/docs/JavaScript/Reference/Global_Objects/JSON/stringify">stringifiable</a> objects</strong>. If you want to stringify a submitted data, use <a href="#A_little_vanilla_framework" title="#A_little_vanilla_framework">the previous <em>pure</em>-AJAX example</a>. Note also that, although in this example there are some <code>file</code> {{ HTMLElement("input") }} fields, <strong>when you submit a form through the <code>FormData</code> API you do not need to use the <a href="/en-US/docs/DOM/FileReader" title="/en-US/docs/DOM/FileReader"><code>FileReader</code></a> API also</strong>: files are automatically loaded and uploaded.</div>

<h2 id="Получить_дату_последнего_изменения">Получить дату последнего изменения</h2>

<pre class="brush: js notranslate">function getHeaderTime () {
  alert(this.getResponseHeader("Last-Modified"));  /* A valid GMTString date or null */
}

var oReq = new XMLHttpRequest();
oReq.open("HEAD" /* use HEAD if you only need the headers! */, "yourpage.html", true);
oReq.onload = getHeaderTime;
oReq.send();</pre>

<h3 id="Do_something_when_last_modified_date_changes">Do something when last modified date changes</h3>

<p>Let's create these two functions:</p>

<pre class="brush: js notranslate">function getHeaderTime () {

  var
    nLastVisit = parseFloat(window.localStorage.getItem('lm_' + this.filepath)),
    nLastModif = Date.parse(this.getResponseHeader("Last-Modified"));

  if (isNaN(nLastVisit) || nLastModif &gt; nLastVisit) {
    window.localStorage.setItem('lm_' + this.filepath, Date.now());
    isFinite(nLastVisit) &amp;&amp; this.callback(nLastModif, nLastVisit);
  }

}

function ifHasChanged(sURL, fCallback) {
  var oReq = new XMLHttpRequest();
  oReq.open("HEAD" /* use HEAD - we only need the headers! */, sURL, true);
  oReq.callback = fCallback;
  oReq.filepath = sURL;
  oReq.onload = getHeaderTime;
  oReq.send();
}</pre>

<p>Test:</p>

<pre class="brush: js notranslate">/* Let's test the file "yourpage.html"... */

ifHasChanged("yourpage.html", function (nModif, nVisit) {
  alert("The page '" + this.filepath + "' has been changed on " + (new Date(nModif)).toLocaleString() + "!");
});</pre>

<p>If you want to know <strong>whether <em>the current page</em> has changed</strong>, please read the article about <a href="/en-US/docs/Web/API/document.lastModified" title="/en-US/docs/Web/API/document.lastModified"><code>document.lastModified</code></a>.</p>

<h2 id="Межсайтовые_XMLHttpRequest">Межсайтовые XMLHttpRequest</h2>

<p>Современные браузеры поддерживают межсайтовые запросы по стандарту <a href="/en-US/docs/HTTP_access_control" title="HTTP access control">Access Control for Cross-Site Requests</a>. Для этого, серверу необходимо дополнительно указывать заголовок <code>origin</code>. В противном случае, выбрасывается исключение <code>INVALID_ACCESS_ERR</code>.</p>

<h2 id="Обход_кеширования">Обход кеширования</h2>

<p>Для кросс-браузерного обхода кеширования в конец URL-запроса достаточно добавить случайную строку в GET-параметры, то есть сразу после «?», например:</p>

<pre class="notranslate">http://foo.com/bar.html -&gt; http://foo.com/bar.html?12345
http://foo.com/bar.html?foobar=baz -&gt; http://foo.com/bar.html?foobar=baz&amp;12345
</pre>

<p>Таким образом, каждый новый запрос будет происходить по новому URL и кеширование страницы не будет производиться.</p>

<p>Автоматизировать этот подход можно следующим сниппетом:</p>

<pre class="brush:js notranslate">var oReq = new XMLHttpRequest();

oReq.open("GET", url + ((/\?/).test(url) ? "&amp;" : "?") + (new Date()).getTime(), true);
oReq.send(null);</pre>

<h2 id="Безопасность">Безопасность</h2>

<p>{{fx_minversion_note(3, "Versions of Firefox prior to Firefox 3 allowed you to set the preference <code>capability.policy.&lt;policyname&gt;.XMLHttpRequest.open&lt;/policyname&gt;</code> to <code>allAccess</code> to give specific sites cross-site access.  This is no longer supported.")}}</p>

<p>{{fx_minversion_note(5, "Versions of Firefox prior to Firefox 5 could use <code>netscape.security.PrivilegeManager.enablePrivilege(\"UniversalBrowserRead\");</code> to request cross-site access. This is no longer supported, even though it produces no warning and permission dialog is still presented.")}}</p>

<p>The recommended way to enable cross-site scripting is to use the <code>Access-Control-Allow-Origin </code> HTTP header in the response to the XMLHttpRequest.</p>

<h3 id="XMLHttpRequests_being_stopped">XMLHttpRequests being stopped</h3>

<p>If you end up with an XMLHttpRequest having <code>status=0</code> and <code>statusText=null</code>, it means that the request was not allowed to be performed. It was <code><a href="http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-unsent" title="http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-unsent">UNSENT</a></code>. A likely cause for this is when the <a href="http://www.w3.org/TR/XMLHttpRequest/#xmlhttprequest-origin" style="outline: 1px dotted; outline-offset: 0pt;"><code>XMLHttpRequest</code> origin</a> (at the creation of the XMLHttpRequest) has changed when the XMLHttpRequest is then <code>open()</code>. This case can happen for example when one has an XMLHttpRequest that gets fired on an onunload event for a window: the XMLHttpRequest gets in fact created when the window to be closed is still there, and then the request is sent (ie <code>open()</code>) when this window has lost its focus and potentially different window has gained focus. The way to avoid this problem is to set a listener on the new window "activate" event that gets set when the old window has its "unload" event fired.</p>

<h2 id="Using_XMLHttpRequest_from_JavaScript_modules_XPCOM_components">Using XMLHttpRequest from JavaScript modules / XPCOM components</h2>

<p>Instantiating <code>XMLHttpRequest</code> from a <a href="/en-US/docs/JavaScript_code_modules/Using" title="https://developer.mozilla.org/en/JavaScript_code_modules/Using_JavaScript_code_modules">JavaScript module</a> or an XPCOM component works a little differently; it can't be instantiated using the <code>XMLHttpRequest()</code> constructor. The constructor is not defined inside components and the code results in an error. The best way to work around this is to use the XPCOM component constructor.</p>

<pre class="brush: js notranslate">const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
</pre>

<p>Unfortunately in versions of Gecko prior to Gecko 16 there is a bug which can cause requests created this way to be cancelled for no reason.  If you need your code to work on Gecko 15 or earlier, you can get the XMLHttpRequest constructor from the hidden DOM window like so.</p>

<pre class="brush:js notranslate">const { XMLHttpRequest } = Components.classes["@mozilla.org/appshell/appShellService;1"]
                                     .getService(Components.interfaces.nsIAppShellService)
                                     .hiddenDOMWindow;
var oReq = new XMLHttpRequest();</pre>

<h2 id="Дополнительные_ссылки">Дополнительные ссылки</h2>

<ol>
 <li><a href="/en-US/docs/AJAX/Getting_Started" title="AJAX/Getting_Started">MDN AJAX introduction</a></li>
 <li><a href="/en-US/docs/HTTP_access_control" title="HTTP access control">HTTP access control</a></li>
 <li><a href="/en-US/docs/How_to_check_the_security_state_of_an_XMLHTTPRequest_over_SSL" title="How to check the security state of an XMLHTTPRequest over SSL">How to check the security state of an XMLHTTPRequest over SSL</a></li>
 <li><a href="http://www.peej.co.uk/articles/rich-user-experience.html">XMLHttpRequest - REST and the Rich User Experience</a></li>
 <li><a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/xmobjxmlhttprequest.asp">Microsoft documentation</a></li>
 <li><a href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html">Apple developers' reference</a></li>
 <li><a href="http://jibbering.com/2002/4/httprequest.html">"Using the XMLHttpRequest Object" (jibbering.com)</a></li>
 <li><a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object: W3C Specification</a></li>
 <li><a href="http://dev.w3.org/2006/webapi/progress/Progress.html" title="http://dev.w3.org/2006/webapi/progress/Progress.html">Web Progress Events specification</a></li>
</ol>