Topic 6 - AJAX and JSON

Today we will cover the following topics:

JavaScript Revision

Remember from the first year that JavaScript is a language to develop interactive, rich client-side applications. Many websites these days are similar to traditional standalone desktop applications (think of Google Docs, etc) and rely on large amounts of JavaScript to implement this functionality.

JavaScript is typically stored in external .js files, and included into web pages with <script> tags. Here is a simple hello world example, which shows a "Hello World" alert box:

function init() {
    alert("Hello World!")
}
You'd save this in an external file, e.g. script.js and then include it in your website:
<script type='text/javascript' src='script.js'></script>

Here is a full web page which runs this JavaScript when the page first loads:

<html>
<head>
<script type='text/javascript' src='script.js'></script>
</head>
<body onload='init()'>
<h1>Test JavaScript Page</h1>
</body>
</html>

Unobtrusive JavaScript: Event Handlers with Arrow Functions

Previously, it was common to link buttons and other user interface elements to JavaScript event handlers (functions which run when the button was clicked) directly within the HTML code. However, this leads to a messy and hard-to-read mix of HTML and JavaScript. It is better, instead, to write unobtrusive JavaScript in which user interface elements are linked to JavaScript event handlers when the page first loads. Here is an example of some JavaScript which does just this. Note how in the init() function (which runs when the page first loads) we link the button with the ID of btn1 to an event handler.

// JavaScript file, script2.js
function init() {
    document.getElementById("btn1").addEventListener("click", clickHandler);
}
    
function clickHandler() {
    alert("You clicked me!");
}
The accompanying HTML might look like this:
<html>
<head>
<script type='text/javascript' src='script2.js'></script>
</head>
<body onload='init()'>
<h1>Test JavaScript Event Handling Page</h1>
<input type='button' id='btn1' value='Click me!' />
</body>
</html>
Note how in our init() function we add an event listener to our button, btn1. An event listener is a function which runs in response to an event (such as the user clicking a button).

Note that addEventListener() takes two parameters:

Arrow functions

Arrow functions are a new feature of the recent JavaScript standard, ECMAScript 6. They are anonymous, nameless functions which can be used as parameters where a callback function is expected. Rather than writing the callback function as a separate function, and specifying the name of the callback as a parameter, we write the function in full where the callback is expected. Here is the JavaScript example above rewritten so that the callback is an arrow function. Note also how we do not use the word "function" with arrow functions, but separate the parameter list and function body with the arrow operator (=>):

// JavaScript file, script2a.js
function init() {
    document.getElementById("btn1").addEventListener("click", ()=> 
        { 
            alert("You clicked me!") 
        } 
    );
}
Notice how this time, rather than creating a named function called clickHandler(), we pass in an anonymous, arrow function as the second parameter to addEventListener(). Note the syntax of the arrow function:
() => 
{
    alert("You clicked me!");
}
The () is the parameter list to the arrow function (here, the function takes no parameters). We follow the parameter list with the arrow operator (=>) and then write the body of the function, beginning and ending with braces { } in the same way as normal functions.

Revisiting AJAX

AJAX applications

AJAX stands for Asynchronous JavaScript and XML. It is a technique which allows browsers to communicate with a web server without having to reload the page.

A user might enter a search term in a form and click Go, and then the search results would be sent back from the server and one small part of the page only (as opposed to the entire page) updated with the search results.

Furthermore, requests to the server are sent, and responses received, in the background without any interruption to the user's interaction with the website... so, unlike in a traditional web application where the user has to wait for a response from the server, the user can continue to interact with the page while waiting for the response to come back.

AJAX is not an actual language, but a combination of technologies used to produce the effect above. An AJAX application typically consists of three components:

The AJAX process typically works as follows:

Writing AJAX code

In ECMAScript 6, a new AJAX API is available - the fetch API. This makes it easier to write AJAX applications compared to previously. However, it does make use of a feature of ECMAScript 6 called promises which we will return to later, and therefore, at the moment might be a little difficult to understand given we haven't covered promises yet.

So for now, we will use the older API ('XMLHttpRequest2') but we will cover the fetch API later.

// JavaScript file, ajax1.js
function init() {
    
        
        // For simplicity, we're specifying that when the button clicks,
        // a regular callback function 'sendAjax' will run, rather than an arrow function.
        // This is because we are using an arrow function in our AJAX code as well, and I don't
        // want to overcomplicate things at this stage by having too many arrow functions inside other arrow functions!
        
        document.getElementById("btn1").addEventListener("click", sendAjax);
            
}
    
function sendAjax() {
        // Read in the input from the two form fields
        var a = document.getElementById('destination').value;
        var b = document.getElementById('date').value;
        
        // Set up our AJAX connection variable (this is an object, for those of you who have done OO programming)
        var ajaxConnection = new XMLHttpRequest();
    
        // Set up the callback function. Here, the callback is an arrow function.
        ajaxConnection.addEventListener ("load",e => 
            { 
                 var output = ""; // initialise output to blank text
                 var allFlights = JSON.parse(e.target.responseText);

                 // Loop through each 
                 allFlights.forEach( curFlight => 
                        {
                            // add the details of the current flight to the "output" variable
                            output = output + `Flight number: ${curFlight.number} Depart: ${curFlight.depart} Arrive: ${curFlight.arrive} <br /> `;
                        } );
                // Put the HTML output into the results div (up to you to do!)        
            });
    
   
        // Open the connection to a given remote URL.
        ajaxConnection.open("GET" , `https://example.com/flights.php?destination=${a}&date=${b}`);
    
        // Send the request.
        ajaxConnection.send();
}
with this HTML:
<html>
<head>
<script type='text/javascript' src='ajax1.js'></script>
</head>
<body onload='init()'>
<h1>Test AJAX Page</h1>
Destination: <input id='destination' />
Date: <input id='date' />
<input type='button' id='btn1' value='Search for flights!!' />
<div id='responseDiv'></div>
</body>
</html>
How does this work?

Using JSON in an AJAX application

We will now discuss what the callback function does, but before we do so we need to discuss how JSON parsing works in JavaScript.

JSON is most commonly used in conjunction with AJAX. An AJAX application can make a request to a server side script which generates JSON from the contents of a database, and then interpret the JSON returned. We can then convert the JSON into JavaScript arrays and objects. (Remember that an object is a code representation of a real-world entity; here, our objects are flights). For example, imagine that this JSON code is returned from the server in the above example if the usersearched for flights from Gatwick to Rome.

[
    {
        "origin" : "Gatwick",
        "destination": "Rome",
        "number" : "EU901",
        "depart"  : "0900",
        "arrive" : "1200"
    } ,

    { 
        "origin" : "Gatwick",
        "destination": "Rome",
        "number" : "EU903",
        "depart"  : "1100",
        "arrive" : "1400"
    } ,

    { 
        "origin" : "Gatwick",
        "destination": "Rome",
        "number" : "EU905",
        "depart"  : "1400",
        "arrive" : "1700"
    }
]
This JSON code, an array of three objects, can be converted into the JavaScript equivalent, a JavaScript array of three JavaScript objects, using the JSON.parse() function. A JavaScript object is similar to a PHP associative array - we will look at objects in more detail later in the unit.

We use the JSON.parse() function to convert JSON into the corresponding JavaScript arrays and objects. We will now return to our callback function (highlighted below):

ajaxConnection.addEventListener ("load",e => 
{ 
    var output = ""; // initialise output to blank text
    var allFlights = JSON.parse(e.target.responseText);
    allFlights.forEach( curFlight => 
        {
            // add the details of the current flight to the "output" variable
            output = output + `Flight number: ${curFlight.number} Depart: ${curFlight.depart} Arrive: ${curFlight.arrive} <br /> `;
    } );
    
});

The same-origin policy

AJAX applications are normally subject to the same-origin policy. This means that the back-end (the server-side script that the JavaScript talks to) must be delivered from the same exact domain as the front-end. The reason for this is security: the ability for an AJAX front end to talk to a third-party server opens up the possibility of cross-site scripting. According to the W3C on their same origin policy document, with the same-origin policy "the overarching intent is to let users visit untrusted web sites without those web sites interfering with the user's session with honest web sites" - in other words, it prevents the possibility of a third-party AJAX-based site using AJAX to communicate with a legitimate site which the user might be currently logged on to, which could lead to stealing of session-specific information.

The domain must be exact; subdomains of a top-level domain are treated as different. For example booking.solentholidays.com and hotels.solentholidays.com are treated as different. This is because several users with different subdomains might be sharing the same top-level domain of a hosting company, e.g. fredsmith.solenthosting.com and timjones.solenthosting.com.

Circumventing the same-origin policy with CORS

There is, however, a way in which server-side developers can circumvent the same origin policy in certain cases. This is done by explicitly allowing, on the server side, certain AJAX clients to connect. A common case is where one person owns two domains, and would like the two domains to communicate with each other over AJAX. For example, Solent Holidays might have two domains, hotels.solentholidays.com and booking.solentholidays.com - and wishes to be able to send users' booking details from booking.solentholidays.com to a hotels web service on hotels.solentholidays.com. To do this, they would have to add code to the server-side scripts on hotels.solentholidays.com to allow booking.solentholidays.com to connect.

This is done by using the technique of Cross-Origin Resource Sharing (CORS). An Access-Control-Allow-Origin line is added to the HTTP response from the PHP script using the header() function, e.g. PHP scripts on hotels.solentholidays.com could include the following:

<?php
header("Access-Control-Allow-Origin: booking.solentholidays.com");
... rest of script ... 

AJAX and JSON Parsing - Exercise

The main exercise is to write a website for HT-Tracks including an AJAX front end which parses (interprets) the JSON that your web service from the first week produced. The Standard Questions are the most important to complete.

Standard Questions

  1. Ensure that your HT-Tracks page (which you should have done as homework) includes a form field to allow the user to enter an artist, and a button, as in the flights example, above.
  2. In a separate JavaScript file, write an AJAX request function which connects to your web service from session 2. You should parse the JSON returned. Place the results within a <div>. Remember that you can use innerHTML to fill a page element, e.g:
    document.getElementById("div1").innerHTML = "New content!";

More advanced questions

  1. Display the results in a table.
  2. Move on to the next topic. If you begin this now, you will have more chance of completing all the advanced questions in that topic.