XMLHttpRequest
makes sending HTTP requests very easy. You simply create an instance of the object, open a URL, and send the request. The HTTP status of the result, as well as the result's contents, are available in the request object when the transaction is completed.
Synchronous and asynchronous requests
XMLHttpRequest
supports both synchronous and asynchronous communications.
Note: You shouldn't use synchronous
XMLHttpRequests
because, due to the inherently asynchronous nature of networking, there are various ways memory and events can leak when using synchronous requests.
In versions of Firefox prior to Firefox 3, synchronous XMLHttpRequest
requests blocked the user interface. In order to make it possible for the user to terminate frozen requests, Firefox 3 no longer does so.
Example: Synchronous request
This example demonstrates how to make a simple synchronous request.
1 | var req = new XMLHttpRequest(); |
5 | dump(req.responseText); |
Line 1 instantiates the XMLHttpRequest
object. Line 2 then opens a new request, specifying that a GET
request will be used to fetch the Mozilla.org home page, and that the operation should not be asynchronous.
Line 3 sends the request. The null
parameter indicates that no body content is needed for the GET
request.
Line 4 checks the status code after the transaction is completed. If the result is 200 -- HTTP's "OK" result -- the document's text content is output to the console.
Example: Non-HTTP synchronous request
Despite its name, XMLHttpRequest
can be used for non-HTTP requests. This example shows how to use it to fetch a file from the local file system.
1 | var req = new XMLHttpRequest(); |
2 | req.open( 'GET' , 'file:///home/user/file.json ' , false ); |
5 | dump(req.responseText); |
The key thing to note here is that the result status is being compared to 0 for success instead of 200. This is because the file
and ftp
schemes do not use HTTP result codes.
Example: Asynchronous request
If you use XMLHttpRequest
from an extension, you should use it asynchronously. In this case, you receive a callback when the data has been received, which lets the browser continue to work as normal while your request is being handled.
01 | var req = new XMLHttpRequest(); |
03 | req.onreadystatechange = function (aEvt) { |
04 | if (req.readyState == 4) { |
05 | if (req.status == 200) |
06 | dump(req.responseText); |
08 | dump( "Error loading page/n" ); |
Line 2 specifies true
for its third parameter to indicate that the request should be handled asynchronously.
Line 3 creates an event handler function object and assigns it to the request's onreadystatechange
attribute. This handler looks at the request's readyState
to see if the transaction is complete in line 4, and if it is, and the HTTP status is 200, dumps the received content. If an error occurred, an error message is displayed.
Line 11 actually initiates the request. The callback routine is called whenever the state of the request changes.
Analyzing and manipulating HTML responseText
If you use XMLHttpRequest
to get the content of a remote HTML webpage, the responseText
will be a string containing a "soup" of all the HTML tags, which can be hard to manipulate and analyze. There are three primary ways of analyzing this HTML soup string
- Safely parsing with nsIScriptableUnescapeHTML will quickly convert the HTML string into DOM, while striping out javascript and other advanced elements, including the
<head>
of the webpage. - RegExp can be used if you always know the content of the HTML
responseText
beforehand. You might want to remove line breaks, if you use RegExp to scan with regard to linebreaks. However, this method is a "last resort" since if the HTML code changes slightly, the method will likely fail. - Using a hidden chrome or content-level iframe to load up the webpage can also be done to then manipulate it as DOM, however there are security risks to giving remote code this level of privileged access , which can cause issues for the review of your addon. For example, if a webpage executes the common "
document.location = redirecttothispage.html
" command on load, this will get interpreted as changing the browser chrome location (document.location
in an extension) as opposed to the webpage location (content.document.location
in an extension), thus destroying all browser components. Alternatively, and somewhat safer, a responseText
string attained through a XMLHttpRequest
can be analyzed using RegExp to remove potential JavaScript problems, then loaded into the hidden iframe that you have set up:
document.getElementById('hiddenXULiframe').contentWindow.document.body.innerHTML = req.responseText
Using FormData objects
The FormData
object lets you compile a set of key/value pairs to send using XMLHttpRequest
. It's 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 submit()
method would use to send the data if the form's encoding type were set to "multipart/form-data".
Creating a FormData object from scratch
You can build a FormData
object yourself, instantiating it then appending fields to it by calling its append()
method, like this:
1 | var formData = new FormData(); |
3 | formData.append( "username" , "Groucho" ); |
4 | formData.append( "accountnum" , 123456); |
5 | formData.append( "afile" , fileInputElement.files[0]); |
7 | var xhr = new XMLHttpRequest(); |
This example builds a FormData
object containing values for fields named "username" and "accountnum", then uses the XMLHttpRequest
method send()
to send the form's data.
Retrieving a FormData object from an HTML form
To construct a FormData
object that contains the data from an existing <form>
, specify that form element when creating the FormData
object:
newFormData = new FormData(someFormElement);
For example:
1 | var formElement = document.getElementById( "myFormElement" ); |
2 | var xhr = new XMLHttpRequest(); |
3 | xhr.open( "POST" , "submitform.php" ); |
4 | xhr.send( new FormData(formElement)); |
You can also add data to the FormData
object between retrieving it from a form and sending it, like this:
1 | var formElement = document.getElementById( "myFormElement" ); |
2 | formData = new FormData(formElement); |
3 | formData.append( "serialnumber" , serialNumber++); |
This lets you augment the form's data before sending it along, to include additional information that's not necessarily user editable on the form.
Sending files using a FormData object
You can also send files using FormData
. Simply include an <input>
element of type "file":
01 | < form enctype = "multipart/form-data" method = "post" name = "fileinfo" id = "fileinfo" > |
02 | < label >Your email address:</ label > |
03 | < input type = "email" autocomplete = "on" autofocus name = "userid" placeholder = "email" required size = "32" maxlength = "64" >< br /> |
04 | < label >Custom file ID:</ label > |
05 | < input type = "text" name = "fileid" size = "12" maxlength = "32" >< br /> |
06 | < label >File to stash:</ label > |
07 | < input type = "file" name = "file" required> |
09 | < div id = "output" ></ div > |
10 | < a href = "javascript:sendForm()" >Stash the file!</ a > |
Then you can send it using code like the following:
02 | var output = document.getElementById( "output" ); |
03 | var data = new FormData(document.getElementById( "fileinfo" )); |
05 | data.append( "CustomField" , "This is some extra data" ); |
07 | var xhr = new XMLHttpRequest(); |
08 | xhr.open( "POST" , "stash.pl" , false ) |
10 | if (xhr.status == 200) { |
11 | output.innerHTML += "Uploaded!<br />" ; |
13 | output.innerHTML += "Error " + xhr.status + " occurred uploading your file.<br />" ; |
Note that this example is directing the output to a Perl CGI script running on the server, and handles HTTP errors, although not prettily.
You can also use FormData
with jQuery if you set the right options:
1 | var fd = new FormData(document.getElementById( "fileinfo" )); |
2 | fd.append( "CustomField" , "This is some extra data" ); |
Handling binary data
Although XMLHttpRequest
is most commonly used to send and receive textual data, it can be used to send and receive binary content.
Receiving binary data
The load_binary_resource()
function shown below loads binary data from the specified URL, returning it to the caller.
1 | function load_binary_resource(url) { |
2 | var req = new XMLHttpRequest(); |
3 | req.open( 'GET' , url, false ); |
5 | req.overrideMimeType( 'text/plain; charset=x-user-defined' ); |
7 | if (req.status != 200) return '' ; |
8 | return req.responseText; |
The magic happens in line 5, which overrides the MIME type, forcing Firefox to treat it as plain text, using a user-defined character set. This tells Firefox not to parse it, and to let the bytes pass through unprocessed.
1 | var filestream = load_binary_resource(url); |
2 | var abyte = filestream.charCodeAt(x) & 0xff; |
The example above fetches the byte at offset x
within the loaded binary data. The valid range for x
is from 0 to filestream.length-1
.
See downloading binary streams with XMLHttpRequest for a detailed explanation. See also downloading files .
Receiving binary data using JavaScript typed arrays
Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) adds a Gecko-specific mozResponseArrayBuffer
property to the XMLHttpRequest object, which contains a JavaScript typed array representing the raw binary contents of the response from the server. This lets you read the binary data without taking any special steps.
01 | var xhr = new XMLHttpRequest(); |
02 | xhr.open( "GET" , "binary_file" , false ); |
05 | buffer = xhr.mozResponseArrayBuffer; |
07 | var byteArray = new Uint8Array(buffer); |
08 | for ( var i=0; i<byteArray.byteLength; i++) { |
This example reads a binary file and interprets it as 8-bit unsigned integers.
Gecko 6 (Firefox 6.0) adds r
esponseType
and response
properties to the XMLHttpRequest object on behalf of the mozResponseArrayBuffer
property.
01 | var xhr = new XMLHttpRequest(); |
02 | xhr.open( "GET" , "binary_file" , false ); |
03 | xhr.responseType = "arraybuffer" ; |
06 | buffer = xhr.response; |
08 | var byteArray = new Uint8Array(buffer); |
09 | for ( var i=0; i<byteArray.byteLength; i++) { |
This example reads a binary file and interprets it as 8-bit unsigned integers.
Sending binary data
This example transmits binary content asynchronously, using the POST
method.
1 | var req = new XMLHttpRequest(); |
2 | req.open( "POST" , url, true ); |
4 | req.setRequestHeader( "Content-Length" , 741); |
5 | req.sendAsBinary(aBody); |
Line 4 sets the Content-Length header to 741, indicating that the data is 741 bytes long. Obviously you need to change this value based on the actual size of the data being sent.
Line 5 uses the sendAsBinary()
method to initiate the request.
You can also send binary content by passing an instance of the nsIFileInputStream
to send()
. In that case, you don't have to set the Content-Length header yourself, as the information is fetched from the stream automatically:
02 | var stream = Components.classes[ "@mozilla.org/network/file-input-stream;1" ] |
03 | .createInstance(Components.interfaces.nsIFileInputStream); |
04 | stream.init(file, 0x04 | 0x08, 0644, 0x04); |
07 | var mimeType = "text/plain" ; |
09 | var mimeService = Components.classes[ "@mozilla.org/mime;1" ] |
10 | .getService(Components.interfaces.nsIMIMEService); |
11 | mimeType = mimeService.getTypeFromFile(file); |
16 | var req = Components.classes[ "@mozilla.org/xmlextras/xmlhttprequest;1" ] |
17 | .createInstance(Components.interfaces.nsIXMLHttpRequest); |
18 | req.open( 'PUT' , url, false ); |
19 | req.setRequestHeader( 'Content-Type' , mimeType); |
Monitoring progress
XMLHttpRequest
provides the ability to listen to various events that can occur while the request is being processed. This includes periodic progress notifications, error notifications, and so forth.
Requires Gecko 1.9.1 (Firefox 3.5 / Thunderbird 3.0 / SeaMonkey 2.0)
In Firefox 3.5 and later
Gecko 1.9.1 (Firefox 3.5 / Thunderbird 3.0 / SeaMonkey 2.0) adds support for DOM progress event monitoring of XMLHttpRequest
transfers; this follows the Web API specification for progress events .
01 | var req = new XMLHttpRequest(); |
03 | req.addEventListener( "progress" , updateProgress, false ); |
04 | req.addEventListener( "load" , transferComplete, false ); |
05 | req.addEventListener( "error" , transferFailed, false ); |
06 | req.addEventListener( "abort" , transferCanceled, false ); |
13 | function updateProgress(evt) { |
14 | if (evt.lengthComputable) { |
15 | var percentComplete = evt.loaded / evt.total; |
22 | function transferComplete(evt) { |
23 | alert( "The transfer is complete." ); |
26 | function transferFailed(evt) { |
27 | alert( "An error occurred while transferring the file." ); |
30 | function transferCanceled(evt) { |
31 | alert( "The transfer has been canceled by the user." ); |
Lines 3-6 add event listeners for the various events that are sent while performing a data transfer using XMLHttpRequest
. See nsIDOMProgressEvent
and nsIXMLHttpRequestEventTarget
for details on these events.
Note: You need to add the event listeners before calling
open()
on the request. Otherwise the progress events will not fire.
The progress event handler, specified by the updateProgress()
function in this example, receives the total number of bytes to transfer as well as the number of bytes transferred so far in the event's total
and loaded
fields. However, if the lengthComputable
field is false, the total length is not known and will be zero.
Progress events exist for both download and upload transfers. The download events are fired on the XMLHttpRequest
object itself, as shown in the above sample. The upload events are fired on the XMLHttpRequest.upload
object, as shown below:
1 | var req = new XMLHttpRequest(); |
3 | req.upload.addEventListener( "progress" , updateProgress, false ); |
4 | req.upload.addEventListener( "load" , transferComplete, false ); |
5 | req.upload.addEventListener( "error" , transferFailed, false ); |
6 | req.upload.addEventListener( "abort" , transferCanceled, false ); |
Note: Progress events are not available for the
file:
protocol.
In Firefox 3 and earlier
If, for example, you wish to provide progress information to the user while the document is being received, you can use code like this:
01 | function onProgress(e) { |
02 | var percentComplete = (e.position / e.totalSize)*100; |
07 | alert( "Error " + e.target.status + " occurred while receiving the document." ); |
14 | var req = new XMLHttpRequest(); |
15 | req.onprogress = onProgress; |
16 | req.open( "GET" , url, true ); |
18 | req.onerror = onError; |
The onprogress
event's attributes, position
and totalSize
, indicate the current number of bytes received and the total number of bytes expected, respectively.
All of these events have their target
attribute set to the XMLHttpRequest
they correspond to.
Note:
Firefox 3 properly ensures that the values of the
target
,
currentTarget
, and
this
fields of the event object are set to reference the correct objects when calling event handlers for XML documents represented by
XMLDocument
. See
bug 198595 for details.
Requires Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0)
Detecting any load end condition
Starting in Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0) , you can detect all three load-ending conditions (abort
, load
, or error
) using the loadend
event:
1 | req.addEventListener( "loadend" , loadEnd, false ); |
3 | function loadEnd(evt) { |
4 | alert( "The transfer finished (although we don't know if it succeeded or not)." ); |
Note that there's no way to be certain from the information received by the loadend
event as to which condition caused the operation to terminate; however, you can use this to handle tasks that need to be performed in all end-of-transfer scenarios.
Cross-site XMLHttpRequest
Firefox 3.5 supports cross-site requests by implementing the web applications working group's Access Control for Cross-Site Requests standard. As long as the server is configured to allow requests from your web application's origin, XMLHttpRequest
will work. Otherwise, an INVALID_ACCESS_ERR
exception is thrown.
Bypassing the cache
Normally, XMLHttpRequest
tries to retrieve content from the cache, if it's available. To bypass this, do the following:
1 | var req = new XMLHttpRequest(); |
2 | req.open( 'GET' , url, false ); |
3 | req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; |
Note: This approach will only work in Gecko-based software, as the
channel
attribute is Gecko-specific.
An alternate, cross-browser compatible approach is to append a timestamp to the URL, being sure to include a "?" or "&" as appropriate. For example:
http://foo.com/bar.html
becomes
http://foo.com/bar.html?12345
and
http://foo.com/bar.html?foobar=baz
becomes
http://foo.com/bar.html?foobar=baz&12345
Since the local cache is indexed by URL, this causes every request to be unique, thereby bypassing the cache.
You can automatically adjust URLs using the following code:
1 | var req = new XMLHttpRequest(); |
2 | req.open( "GET" , url += (url.match(//?/) == null ? "?" : "&" ) + ( new Date()).getTime(), false ); |
Security
Firefox 3 note
Versions of Firefox prior to Firefox 3 allowed you to set the preference capability.policy..XMLHttpRequest.open to allAccess to give specific sites cross-site access. This is no longer supported.
Using XMLHttpRequest from JavaScript modules / XPCOM components
Instantiating XMLHttpRequest
from a JavaScript module or an XPCOM component works a little differently; it can't be instantiated using the XMLHttpRequest()
constructor. The constructor is not defined inside components and the code results in an error. You'll need to create and use it using a different syntax.
Instead of this:
1 | var req = new XMLHttpRequest(); |
2 | req.onprogress = onProgress; |
5 | req.open( "GET" , url, true ); |
Do this:
1 | var req = Components.classes[ "@mozilla.org/xmlextras/xmlhttprequest;1" ] |
2 | .createInstance(Components.interfaces.nsIXMLHttpRequest); |
3 | req.onprogress = onProgress; |
6 | req.open( "GET" , url, true ); |
For C++ code you would need to QueryInterface
the component to an nsIEventTarget
in order to add event listeners, but chances are in C++ using a channel directly would be better.
See also
- MDC AJAX introduction
- HTTP access control
- How to check the security state of an XMLHTTPRequest over SSL
- XMLHttpRequest - REST and the Rich User Experience
- Microsoft documentation
- Apple developers' reference
- "Using the XMLHttpRequest Object" (jibbering.com)
- The XMLHttpRequest Object: W3C Working Draft
- Web Progress Events specification
- Reading Ogg files with JavaScript (Chris Double)