Learn JavaScript: Novice to Ninja

Chapter 13: Ajax

Ajax is a technique that allows web pages to communicate asynchronously with a server, and it dynamically updates web pages without reloading. This enables data to be sent and received in the background, as well as portions of a page to be updated in response to user events, while the rest of the program continues to run.

The use of Ajax revolutionized how websites worked, and ushered in a new age of web applications. Web pages were no longer static, but dynamic applications.

In this chapter, we’ll cover the following topics:

  • Clients and servers
  • A brief history of Ajax
  • Communicating with the server using the Fetch API
  • Receiving data with Ajax
  • Sending data with Ajax
  • Form data
  • Our project ― obtain questions using Ajax

Clients and Servers

The web of computers known as the internet can be separated into two parts: clients and servers. A client, such as a web browser, will request a resource (usually a web page) from a server, which processes the request and sends back a response to the client.

JavaScript was originally designed as a client-side scripting language, meaning that it ran locally in the browser, adding dynamic features to the web page that was returned from the server. Ajax allows JavaScript to request resources from a server on behalf of the client. The resources requested are usually JSON data or small fragments of text or HTML rather than a whole web page.

Consequently, a server is required when requesting resources using Ajax. Typically this involves using a server-side language, such as PHP, Ruby, Node.js, or .NET to serve the data response following an Ajax request (usually from a back-end database). To practice using Ajax, you can either set up a local development server on your own computer, or request the files from an external website that uses cross-origin resource sharing (CORS) in order to avoid the same-origin policy that browsers enforce. All the examples in this chapter can be run without having to set up a local development server, although it may be worth looking into if you wish to do a lot of Ajax or server-side development.

Same-Origin Policy

The same-origin policy in browsers blocks all requests from a domain that is different from the page making the request. This policy is enforced by all modern browsers and is to stop any malicious JavaScript being run from an external source. The problem is that the APIs of many websites rely on data being transferred across domains.

Cross-origin resource sharing (CORS)is a solution to this problem as it allows resources to be requested from another website outside the original domain. The CORS standard works by using HTTP headers to indicate which domains can receive data. A website can have the necessary information in its headers to allow external sites access to its API data. Most modern browsers support this method and respect the restrictions specified in the headers.

A Brief History of Ajax

When the World Wide Web started, web pages contained static content. Any changes to the content on the page required a full page reload, often resulting in the screen going blank while the new page loaded. Remember, this was back in the 1990s, when dial-up modems were the norm.

In 1999, Microsoft implemented the XMLHTTP ActiveX control in Internet Explorer 5. It was developed initially for the Outlook web client, and allowed data to be sent asynchronously in the background using JavaScript. Other browsers implemented this technique, although it remained a relatively unknown feature, and was rarely used.

Asynchronous loading techniques started to be noticed when Google launched Gmail and Google Maps in 2004 and 2005 respectively. These web applications used asynchronous loading techniques to enhance the user experience by changing the parts of the page without a full refresh. This gave them a much snappier and responsive quality that felt more like a desktop application.

The term ‘Ajax’ was coined by Jesse James Garrett in 2005 in the article“Ajax: A New Approach to Web Applications,”where he referred to techniques being used by Google in its recent web applications. Ajax was a neat acronym that referred to the different parts of the process being used: Asynchronous JavaScript and XML:AsynchronousWhen a request for data is sent, the program doesn’t have to stop and wait for the response. It can carry on running, waiting for an event to fire when a response is received. By using callbacks to manage this, programs are able to run in an efficient way, avoiding lag as data is transferred back and forth.JavaScriptJavaScript was always considered a ‘front-end’ language, not used to communicate with the server. Ajax enabled JavaScript to send requests and receive responses from a server, allowing content to be updated in real time.XMLWhen the term Ajax was originally coined, XML documents were often used to return data. Many different types of data can be sent, but by far the most commonly used in Ajax nowadays is JSON, which is more lightweight and easier to parse than XML. (Although it has never really taken off, the termAjaj is sometimes used to describe the technique.)JSON also has the advantage of being natively supported in JavaScript, so you can deal with JavaScript objects rather than having to parse XML files using DOM methods.

After the publication of Garrett’s article, Ajax use really started to take off. Now users could see new content on web pages without having to refresh the page. Shopping baskets could be updated in the background, partial page content could be loaded seamlessly, and photo galleries could dynamically load images.

Today, it’s unusual for Ajax not to be used when a partial web page update is required. The explosion in the use of public APIs also means that Ajax is used more than ever to transport data back and forth between sites.

APIs

An application programming interface (API) is a collection of methods that allows external access to another program or service. Many websites allow controlled access to their data via public APIs. This means that developers are able to interact with the data and create mashups of third-party services. A weather site, for example, might have an API that provides methods that return information about the weather in a given location, such as temperature, wind speed, and so on. This can then be used to display local weather data on a web page. The information that’s returned by APIs is often serialized as JSON. Since the data is being provided by an external site, CORS will have to be enabled in order to access information from an API. Some services may also require authentication in order to access their APIs.

The Fetch API

The XMLHttpRequest object was finally standardized by the WHATWG and W3C as part of the HTML5 specification, despite it originally being implemented by Microsoft many years earlier, and already available in most browsers.

It has since been superseded by theFetch API, which is currently a living standard for requesting and sending data asynchronously across a network. The Fetch API uses promises to avoid callback hell, and also streamlines a number of concepts that had become cumbersome when using the XMLHttpRequest object.

We’re going to start by taking a look at how the Fetch API works and the different interfaces that it uses. After this we’ll build a page that demonstrates the ideas we’ve looked at.

Basic Usage

The Fetch API provides a global fetch() method that only has one mandatory argument, which is the URL of the resource you wish to fetch. A very basic example would look something like the following piece of code:

fetch('https://example.com/data').then( // code that handles the response ).catch( // code that runs if the server returns an error )

As you can see, the fetch() method returns a promise that resolves to the response returned from the URL that was provided as an argument. In the example above, the promise will be resolved when a response is received from the URL ‘https:example.com/data’. Because it’s a promise, we can also use a catch statement at the end to deal with any errors that may occur.

Response Interface

The Fetch API introduced the Response interface that deals with the object that’s returned when the promise is fulfilled. Response objects have a number of properties and methods that allow us to process the response effectively.

For example, each response object has an ok property that checks to see if the response is successful. This is based on theHTTP status code, which can be accessed using the status property. This will usually be 200 if the response was successful, 201 if a resource was created, or 204 when the request is successful but no content is returned. The ok property will return true if the status property is between 200 and 299. We need to manually check if this happens because the promise will only be rejected in the case of a network error, rather than something like a ‘404 page not found error’, which is still considered a successful request in terms of the promise.

This means that we can use an if block to check if the request was successful, and throw an error otherwise:

const url = 'https:example.com/data';fetch(url).then((response) => {    if(response.ok) {        return response;    }    throw Error(response.statusText);}).then( response => // do something with response ).catch( error => console.log('There was an error!') )

Notice that the error thrown refers to the statusText property of the response object and specifies the status message that corresponds to the code returned, for example it might be ‘Forbidden’ for a status code of 403.

Some other properties of the Response object are:

  •  headers – A Headers object (see later section) containing any headers associated with the response
  •  url – A string containing the URL of response
  •  redirected – A boolean value that specifies if the response is the result of a redirect
  •  type – A string value of ‘basic’, ‘cors’, ‘error’ or ‘opaque’. A value of ‘basic’ is used for a response from the same domain. A value of ‘cors’ means the data was received from a valid cross-origin request from a different domain. A value of ‘opaque’ is used for a response received from ‘no-cors’ request from another domain, which means access to the data will be severely restricted. A value of ‘error’ is used when a network error occurs.

The response object also contains a number of methods that return promises that can then be chained together.

Redirects

The redirect() method can be used to redirect to another URL. It creates a new promise that resolves to the response from the redirected URL.

Here is an example of how a redirect response promise would be resolved:

fetch(url).then( response => response.redirect(newURL)); // redirects to another URL.then( // do something else ).catch( error => console.log('There was an error: ', error))

At the present time, there is no support for the redirect() method in any browser.

Text Responses

The text() method takes a stream of text from the response, reads it to completion and then returns a promise that resolves to a USVSting object that can be treated as a string in JavaScript.

Here is an example of how a text response promise would be resolved:

fetch(url).then( response => response.text() ); // transforms the text stream into a JavaScript string.then( text => console.log(text) ).catch( error => console.log('There was an error: ', error))

In this example, once the promise has been resolved, we use the string() method to return a promise that resolves with a string representation of the text that was returned. In the next statement, we take the result of the promise and use console.log() to display the text in the console.

File Responses

The blob() method is used to read a file of raw data, such as an image or a spreadsheet. Once it has read the whole file, it returns a promise that resolves with a blob object.

Here is an example of how a file response promise would be resolved:

fetch(url).then( response => response.blob() ); // transforms the data into a blob object.then( blob => console.log(blob.type) ).catch( error => console.log('There was an error: ', error))

This example is similar to the text example above, but we use the blob() method to return a blob object. We then use the type property to log the MIME-type to log what type of file we have received.

JSON Responses

JSON is probably the most common format for AJAX responses. The json() method is used to deal with these by transforming a stream of JSON data into a promise that resolves to a JavaScript object.

Here is an example of how a JSON response promise would be resolved:

fetch(url).then( response => response.json() ); // transforms the JSON data into a JavaScript object.then( data => console.log(Object.entries(data)) ).catch( error => console.log('There was an error: ', error))

Again, this is very similar to the earlier examples, except this response returns some JSON data that is then resolved as a JavaScript object. This means we can manipulate the object using JavaScript. In the example below, the Object.entries() method is used to view the key and value pairs in the returned object.

Creating Response Objects

Although most of the time you will be dealing with a response object that is returned from a request you make, you can also create your own response objects using a constructor function:

const response = new Response( 'Hello!', {    ok: true,    status: 200,    statusText: 'OK',    type: 'cors',    url: '/api'});

The first argument is the data that is to be returned (for example a text stream, file or JSON data). The second argument is an object that can be used to provide values for any of the properties listed above.

These can be useful to use if you are creating an API that needs to send a response, or if you need to send a dummy response for testing purposes.

Request Interface

We can get more fine-grained control over the request being made by providing a Request object as an argument. This allows a number of options to be set about the request.

Request objects are created using the Request() constructor, and include the following properties:

  •  url – The URL of the requested resource (the only property that is required).
  •  method – a string that specifies which HTTP method should be used for the request. By default, this is ‘GET’.
  •  headers – This is a Headers object (see later section) that provides details of the request’s headers.
  •  mode – Allows you to specify if CORS is used or not. CORS is enabled by default.
  •  cache – Allows you to specify how the request will use the browser’s cache. For example, you can force it to request a resource and update the cache with the result, or you can force it to only look in the cache for the resource.
  •  credentials – Lets you specify if cookies should be allowed with the request.
  •  redirect – Specifies what to do if the response returns a redirect. There’s a choice of three values: ‘follow’ (the redirect is followed), ‘error’ (an error is thrown) or ‘manual’ (the user has to click on a link to follow the redirect).

Hypertext Transfer Protocol

The Web is built upon the Hypertext Transfer Protocol, or HTTP. When a client (usually a browser) makes a request to a server, it contains information about which HTTP verb to use.HTTP verbs, also known as HTTP methodsare the what HTTP uses to tell the server what type of request is being made, which then determines the server will deal with the request.

The five most commonly used verbs when dealing with resources on the web are:

  • GET requests to retrieve resources
  • POST requests, usually used to create a resource but can actually perform any task
  • PUT requests toupsert, which means insert a resource or update it entirely
  • PATCH requests to make partial updates to a resource
  • DELETE requests to delete a resources.

By default, a link in a web page will make a GET request. Forms are also submitted using a GET request by default, but they will often use a POST request.

Thisexcellent blog postby Rob Miller explains each of these verbs in more depth if you’re interested in learning more about them.

A constructor function is used to create a new Request object. An example is shown below:

const request = new Request('https://example.com/data', {    method: 'GET',    mode: 'cors',    redirect: 'follow',    cache: 'no-cache'});

The url property is the first argument, and is required. The second argument is an object made up of any of the other properties listed above.

Once the Request object is assigned to a variable, it can then be used as the parameter of the fetch() method:

fetch(request).then( // do something with the response ).catch( // handle any errors)

Alternatively, you can enter the URL and object directly as arguments of the fetch() method, without having to create a Request object:

fetch('https://example.com/data', {    method: 'GET',    mode: 'cors',    redirect: 'follow',    cache: 'no-cache'}).then( // do something with the response ).catch( // handle any errors)

Headers Interface

HTTPheadersare used to pass on any additional information about a request or response. Typical information contained in headers includes the file-type of the resource, cookie information, authentication information and when the resource was last modified.

The Fetch API introduced a Headers interface, which can be used to create a Headers object, which can then be added as a property of Request and Response objects.

A new Headers instance is created using a constructor function, as seen in the example below:

const headers = new Headers();

The constructor function can be provided with an optional argument containing any initial header values:

const headers = new Headers({ 'Content-Type': 'text/plain', 'Accept-Charset' : 'utf-8', 'Accept-Encoding':'gzip,deflate' })

Headers object includes the following properties and methods that can be used to access information about the headers, as well as edit the header information.

 has() – Can be used to check if the headers object contains the header provided as an argument.

For example:

headers.has('Content-Type');<< true

 get() – Returns the value of the header provided as an argument

For example:

headers.get('Content-Type');<< 'text/plain'

 set() – Can be used to set a value of an already existing header, or create a new header with the value provided as an argument if it does not already exist.

For example:

headers.set('Content-Type', 'application/json');

 append() – Adds a new header to the headers object.

For example:

headers.append('Accept-Encoding','gzip,deflate');

 delete() – Removes the header provided as an argument.

For example:

headers.delete('Accept-Encoding')

 keys()values() and entries() – Iterators that can be used to iterate over the headers key, values or entries (key and value pairs).

For example:

for(const entry of headers.entries(){console.log(entry);}<< [ 'Content-Type', 'application/json' ]

Putting It All Together

We can use the Headers, Request and Response objects to put together a typical example that sets up the URL, Request and Headers before calling the fetch() method:

const url = 'https:example.com/data';const headers = new Headers({ 'Content-Type': 'text/plain', 'Accept-Charset' : 'utf-8', 'Accept-Encoding':'gzip,deflate' })const request = (url,{    headers: headers})fetch(request).then( function(response) {    if(response.ok) {        return response;    }    throw Error(response.statusText);}).then( response => // do something with response ).catch( error => console.log('There was an error!') )

Receiving Information

To demonstrate how to update a web page using Ajax, we’ll need to set up a demonstration page. Create a file called ‘ajax.html’ that contains the following code:

<!doctype html><html lang='en'><head><meta charset='utf-8'><title>Ajax Example</title></head><body><button id='number'>Number Fact</button><button id='chuck'>Chuck Norris Fact</button><div id='output'>    Ajax response will appear here</div><script src='main.js'></script></body>

This is a standard HTML5 web page that contains two buttons and a <div> element. Each button will be used to make a different type of Ajax request. One will request plain text and the other will request a JSON string from an external API. The div with an id of ‘output’ will be where we’ll insert the response we receive from the Ajax request.

For our Ajax requests, we’ll be using a couple of online APIs. The first isNumbersAPI, which returns facts about random numbers as a text string. The second ischucknorris.io, which returns a JSON string, containing a random satirical factoid about everybody’s favorite hard man, Chuck Norris.

Not All These “Facts” Are Safe For Work

Some of the ‘facts’ returned by chucknorris.io can be mildly offensive and use inappropriate language, so proceed with caution!

Now we need a JavaScript file. This should be called main.js and can be saved in the same directory as the other files. Add the following code to start with:

const textButton = document.getElementById('number');const apiButton = document.getElementById('chuck');const outputDiv = document.getElementById('output');

This assigns each of the buttons in the HTML file to a variable, so we can refer to them later in the file.

Next, we’ll assign some URLs to variables:

const textURL = 'http://numbersapi.com/random';const apiURL = 'https://api.chucknorris.io/jokes/random';

And finally, we’ll assign an event handler to each button. Let’s start with the Number Fact button:

textButton.addEventListener('click', () => {    fetch(textURL)    .then( response => {        outputDiv.innerHTML = 'Waiting for response...';    if(response.ok) {        return response;    }        throw Error(response.statusText);    })    .then( response => response.text() )    .then( text => outputDiv.innerText = text )    .catch( error => console.log('There was an error:', error))},false);

This uses the format we saw earlier to construct a fetch request. This returns a promise that resolves to a string. We can then place that string inside the <div> with an id of ‘output’ by assigning it its innerText property.

A number fact

And now for the Chuck Norris Fact button:

apiButton.addEventListener('click', () => {    fetch(apiURL)    .then( response => {        outputDiv.innerHTML = 'Waiting for response...';    if(response.ok) {        return response;    }    throw Error(response.statusText);    })    .then( response => response.json() )    .then( data => outputDiv.innerText = data.value )    .catch( error => console.log('There was an error:', error))},false);

This is almost identical to the Number example, except the response returns JSON, so we use the json() method to return a promise that resolves as a JavaScript object. This object has a value property that contains the Chuck Norris fact, so we insert it into the <div> with an id of ‘output’ using innerText again.

This example shows how easy it is to request data from a server, then insert it into a web page, although there are some subtle differences depending on what type of data is returned.

Spinners

In the previous example we displayed a message to say we were waiting for a response. It is common for sites to use spinners (or egg timers in the old days!) to indicate that the site is waiting for something to happen.Ajax LoadandPreloaders.netare both good resources for creating a spinner graphic for your site.

Let’s try this out. Open ‘ajax.html’ in a browser and try pressing each button. You should see a similar sight to the screenshot below:

A Chuck Norris “Fact”

Sending Information

We can also use Ajax to send information. This can be a variety of formats, but is usually a JSON string.

To illustrate this, we’re going to create a very simple To Do list application that sends information about a task to a server in JSON format, then receives a response to confirm that the task has been saved on a server.

Unfortunately, we don’t have a database to save our tasks to, so we’re going to have to use a dummy site calledJSONPlaceholder. This spoofs the process of sending JSON data to a server, then receiving JSON data in response. It has a number of fake APIs that can be used to create fake examples of posts, comments, albums, photos, todos and users. We’ll be using the fake todo API.

To get started, create an HTML document called ‘todo.html’ that contains the following code:

<!doctype html><html lang='en'><head>    <meta charset='utf-8'>    <title>To Do List</title></head><body>    <form id='todo' action='https://jsonplaceholder.typicode.com/todos' method='POST'>        <input type='text' name='task'  placeholder='Add Task' autofocus required>        <button type='submit'>Add Task</button>    </form>    <script src='main.js'></script></body></html>

This is a simple HTML page that contains a form with a text input element for adding a task, and a button to submit it.

A simple To-Do List

Next, we need to create a JavaScript file called main.js and add the following code:

const form = document.forms['todo'];form.addEventListener('submit', addTask, false);function addTask(event) {    event.preventDefault();    const number = form.task.value;    const task = {        userId: 1,        title: form.task.value,        completed: false    }    const data = JSON.stringify(task);    const url = 'https://jsonplaceholder.typicode.com/todos';    const headers = new Headers({        'Accept': 'application/json',        'Content-Type': 'application/json'    });    const request = new Request(url,    {        method: 'POST',        header: headers,        body: data    }    )    fetch(request)    .then( response => response.json() )    .then( task => console.log(`Task saved with an id of ${task.id}`) )    .catch( error => console.log('There was an error:', error))}

This code creates an event listener that first of all prevents the default behavior of the form, so it doesn’t get submitted when the Add Task button is clicked. Next it creates a task object with a title property that is taken from what was entered in the form. It also has a completed property that has a default value of false . This object is then transformed into a JSON string using the JSON.stringify method and assigned to the variable data .

After this, we build the Headers and Request objects. Because we are sending JSON, we need to add headers of ‘Accept’: ‘application/json’ and ‘Content-Type’: ‘application/json’. Because we aresendingdata, we need to ensure that the method property of the request object is POST so that a POST request is used to send the data. The most important property of the request object is body – this is where the data we want to send is placed. We use the data variable here, so that JSON is sent to the server.

Then we use the fetch() method to send the request and deal with the response. This creates a promise that resolves to a JSON object, so we use the json() method to create another promise that resolves to a JavaScript object. This object has a single property of id to mimic successfully saving the task to a database (as this would result in it being assigned an ID by the database).

We can use this to log a message to the console that refers to the id property that was returned.

If you open up the ‘todo.html’ file, add a task in the form and then submit it, you should see a message in the console similar to the one below.

<< Task saved with an id of 201

This fakes the fact that the task has been saved to a database and the relevant data has been returned. In reality, the data hasn’t been saved, and the ID property has just been randomly generated for demonstration purposes.

Not Quite A Realistic Example

If this was a real live site that was saving to an actual database, you would probably expect more data to be returned than just the id , including more information about the task itself, such as a timestamp of when it was created.

Most forms will have an action attribute that specifies the URL to use if the form is sent without using Ajax. It will also have a method attribute that will specify the HTTP verb to use. These methods are available as properties of the form object, so we could use these properties to create a more generalized request object, as follows:

const request = new Request(form.action,{    method: form.method,    header: headers,    body: data})

 FormData

The Fetch API includes the FormData interface, which makes it much easier to submit information in forms using Ajax.

FormData instance is created using a constructor function:

const data = new FormData();

If a form is passed to this constructor function as an argument, the form data instance will serialize all the data automatically, ready to be sent using Ajax. In our last example, we created the task manually based on the data provided in the form. The FormData interface helps to reduce the amount of code needed when submitting forms.

We can use this to cut down the amount of code in main.js by changing it to the following:

const form = document.forms['todo'];form.addEventListener('submit', addTask, false);function addTask(event) {    event.preventDefault();    const task = new FormData(form);    const url = `http://echo.jsontest.com/id/1/title/${form.task.value}`;    const headers = new Headers({        'Accept': 'application/json',        'Content-Type': 'application/json'    });    const request = new Request(url,    {        method: 'POST',        mode: 'cors',        header: headers,        body: JSON.stringify(task)    }    )    fetch(request)    .then( response => response.json() )    .then( data => console.log(`${data.title} saved with an id of ${data.id}`) )    .catch( error => console.log('There was an error:', error))}

In this function, we create a new FormData instance using the FormData() constructor function and provide the form as an argument. This does all the hard work of creating the task object for us.

It’s also possible to add data to the form data instance as key-value pairs using the append() method:

data = new FormData(); // no form provided as an argument creates an empty form data instancedata.append('height', 75);

The FormData interface really comes into its own when a form contains files to upload. This was a notoriously difficult task in the past, often requiring the use of Flash or another third-party browser plugin to handle the upload process. The FormData instance will automatically create the necessary settings required, and take care of all the hard work if any file uploads are present in the form.

You can find more information about the FormData interface inthis SitePoint article by Craig Bucklerand on theMozilla Developer Network.

A Living Standard

The Fetch API is, at the time of writing, what is known as a ‘living standard’, which means that the specification is being developed ‘in the wild’. This means that, despite it being available to use, it’s still subject to change as developers, browser vendors and end-users provide feedback about how it works. It’s an experimental technology, and new features might get added, or the syntax and behavior of some properties and methods might change in the future. Don’t let this put you off though – living standards often stay relatively stable, especially once they are implemented in browser engines. The latest versions of most browsers already support it (all, except Internet Explorer, anyway), but you should check the level of support before using it in production. By using it you are helping to develop future standards. Just make sure you keep up-to-date with the current specification.

If you don’t want to ‘live on the edge’, you could consider using a library to take care of Ajax requests. The advantage of this approach is that the library will take care of any implementation details behind the scenes – it will use the most up-to-date methods, such as the fetch API, if it’s supported, and fallback on using older methods, if required.

The jQuery library is a good option for this – it has the generic ajax() method that can be used in a very similar way to the fetch() method. For example, if you want to get the data from the number API, you would use the following code:

$.ajax('http://numbersapi.com/random').done(text => outputDiv.innerHTML = text );

For more information, see thejQuery documentation.

Quiz Ninja Project

We can use Ajax to fetch the questions from a server, instead of keeping them in an object inside our JavaScript file. First of all, we need to remove the array of objects stored in the quiz variable at the start of the main.js file, and transfer the information into a separate file. This information has been saved in the JSON format on SitePoint’s S3 account, and can be found at the following URL (it also contains lots more questions than the three we’ve been using so far): http://spbooks.github.io/questions.json

To access this JSON data, use the Fetch API. Add the following code to the top of file:

const url = 'http://spbooks.github.io/questions.json';fetch(url).then(res => res.json()).then(quiz => {    view.start.addEventListener('click', () => game.start(quiz.questions), false);    view.response.addEventListener('click', (event) => game.check(event), false);});

First of all we create a variable called url to store a reference to the URL. Then we use the Fetch API, which returns a promise. If this is successful, then we use the json() method, which returns the data as a JavaScript object. If this is successful, then we register the two event handlers that were initially at the end of the file (these need removing from the end of the file). This means the ‘start’ button won’t work until the data has finished loading.

Everything else in the file stays the same. Keeping the quiz data in a separate file and loading it using Ajax is beneficial as it keeps the question data separate from the actual application logic. It means it’s much easier to edit all in one place. It also means we could potentially create lots of different JSON quiz files that could be linked to, enabling a variety of different quizzes to be played.

Ajaxed Quiz Ninja

You can see a live example onCodePen.

Chapter Summary

  • Ajax is a technique for sending and receiving data asynchronously in the background.
  • The data can be sent in many forms, but it is usually in JSON.
  • Ajax can be used for making partial page updates without having to do a full page reload.
  • Ajax can be used for communicating with external APIs.
  • Ajax requests can be made using the Fetch API.
  • The Response interface allows you to control the response received from a request or to create your own response objects.
  • The Request interface allows you to create a request object that contains information about the request being made, such as the URL and headers.
  • The Headers interface allows you to create HTTP headers that can be added to a request or response object.
  • Requests can retrieve data using a GET request, or send data using a POST request.
  • The FormData interface makes it easier to send data from forms.

In the next chapter, we’ll look at some APIs that comprise part of the HTML5 specification, then learn how to implement them.

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16