Page tree
Skip to end of metadata
Go to start of metadata

Most of the content has been taken from Mozilla's Developer Network Using XMLHttpRequest and slightly modified with compatibility with Smartface.

XMLHttpRequest

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. This page outlines some of the common and even slightly obscure use cases for this powerful JavaScript object.

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

var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();

Types of requests

A request made via XMLHttpRequest can fetch the data in one of two ways, asynchronously or synchronously. The type of request is dictated by the optional async argument (the third argument) that is set on the XMLHttpRequest open() method. If this argument is true or not specified, the XMLHttpRequest is processed asynchronously, otherwise the process is handled synchronously. In general, you should rarely if ever use synchronous requests.

Handling responses

There are several types of response attributes defined by the W3C specification for XMLHttpRequest.  These tell the client making the XMLHttpRequest important information about the status of the response. Some cases where dealing with non-text response types may involve some manipulation and analysis as outlined in the following sections.

Analyzing and manipulating the responseXML property

If you use XMLHttpRequest to get the content of a remote XML document, the responseXML property will be a DOM Object containing a parsed XML document, which can be hard to manipulate and analyze. There are four primary ways of analyzing this XML document:

  1. Using XPath to address (point to) parts of it.

  2. Manually Parsing and serializing XML to strings or objects.

  3. Using XMLSerializer to serialize DOM trees to strings or to files.

  4. RegExp can be used if you always know the content of the XML document 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 XML code changes slightly, the method will likely fail.

Analyzing and manipulating a responseText property containing an XML/HTML document

If you use XMLHttpRequest to get the content of a remote HTML webpage, the responseText property is 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:

  1. Use the XMLHttpRequest.responseXML property.
  2. Inject the content into the body of a document fragment via fragment.body.innerHTML and traverse the DOM of the fragment.
  3. 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.

Analyzing and manipulating a responseText property containing an JSON data

  1. Use JSON.parse method
  2. RegExp can be used if you always know the content of the JSON 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 JSON datachanges slightly, the method will likely fail.

Response property

response property of the XMLHttpRequest is filled based on the "Content-Type" header value of the response. If the Content-Type header value contains one of those values, then the response property is a string; otherwise it is a Blob.

  • application/javascript
  • application/x-javascript
  • application/ecmascript
  • application/xml
  • application/json
  • text/      (starting with some text)

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. There are several well tested methods for coercing the response of an XMLHttpRequest into sending binary data. These involve utilizing the .overrideMimeType() method on the XMLHttpRequest object and is a workable solution.

var oReq = new XMLHttpRequest();
oReq.open("GET", url);
// retrieve data unprocessed as a binary string
oReq.overrideMimeType("text/plain; charset=x-user-defined");
/* ... */
var oReq = new XMLHttpRequest();

oReq.onload = function(e) {
  var blob = oReq.response; // not responseText
  /* ... */
}
oReq.open("GET", url);
oReq.send();

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.

var oReq = new XMLHttpRequest();

oReq.onload = transferComplete;
oReq.onerror = transferFailed;

oReq.open();

// ...

function transferComplete(evt) {
  alert("The transfer is complete.");
}

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

Events are required to be assigned before calling open() on the request.  Otherwise the progress events may not fire.

Sending data and uploading files

Instances of XMLHttpRequest can be used to send data in two ways:

  • using nothing but AJAX like requests individually
  • using the FormData API

The second way (using the FormData API) is the simplest and the fastest, but has the disadvantage that the data collected can not be stringified.
The first way is instead the most complex but also lends itself to be the most flexible and powerful.

Using nothing but XMLHttpRequest

Submitting forms without the FormData API does not require other APIs for most use cases.

Sending File directly
var oReq = new XMLHttpRequest();
oReq.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
var fileToUpload = new SMF.IO.File(SMF.IO.applicationDataDirectory, "myUploadFile.txt");
oReq.open("PUT");
oReq.send(fileToUpload);
Sending file instance over XMLHttpRequest requires the HTTP method to be PUT. If the method is not put, system will override and make the request with PUT method instead.

The same with Blob.

Sending Blob data
var oReq = new XMLHttpRequest();
oReq.setRequestHeader("Content-Type", "binary/octet-stream");
var file = new SMF.IO.File(path),
readerStream = file.openStream(SMF.IO.SteamType.READ),
var blobData = readerStream.readBlob(); //returns blob type content
readerStream.close();
oReq.open("PUT", url);
oReq.send(blobData);

With sending Blob, binary data is sent as binary.

Using FormData objects

The FormData constructor lets you compile a set of key/value pairs to send using XMLHttpRequest. 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 submit() 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 Using FormData Objects page.

var img = new SMF.Image('avatar.png');
var formdata = new FormData();
formdata.append("username", "Smartface"); //fill the username field in the form
formdata.append("file1", img.getBlob(), "avatar.png"); //fill the file1 field in the form
var oReq = new XMLHttpRequest();
oReq.setRequestHeader("Content-Type", "multipart/form-data");
oReq.open("PUT", url);
oReq.send(formdata);

Get last modified date

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");
oReq.onload = getHeaderTime;
oReq.send();

Do something when last modified date changes

function getHeaderTime () {

  var nLastVisit = parseFloat(SMF.getData('lm_' + this.filepath));
  var nLastModif = Date.parse(this.getResponseHeader("Last-Modified"));

  if (isNaN(nLastVisit) || nLastModif > nLastVisit) {
    SMF.setData('lm_' + this.filepath, Date.now());
    isFinite(nLastVisit) && this.callback(nLastModif, nLastVisit);
  }

}

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

Test:

/* Let's test the file "yourdata.json"... */

ifHasChanged("yourdata.json", function (nModif, nVisit) {
  alert("The data '" + this.filepath + "' has been changed on " + (new Date(nModif)).toLocaleString() + "!");
});

Differences and not implemented parts of XMLHttpRequest

  • responseType property is working differently
  • open does not implement user and password optional parameters
  • not implemented:
    • responseType
    • upload
    • withCredentials
    • addEventListener