Topic 7 - Using the HTML DOM
Further AJAX topics
The main topic this week is the DOM but before we look at that,
we just need to cover a couple of additional AJAX topics related to our
Web Services and HTTP topic from last week, namely:
- How to retrieve an HTTP status code from an AJAX client;
- How to send POST requests and
HTTP authentication from an AJAX client.
How to test the HTTP status code from an AJAX client
It is easy to test the HTTP status code from a web service client. If you are using AJAX you can simply use
the status property of e.target (the original XMLHttpRequest object) in the callback, for example:
ajaxConnection.addEventListener("load", e => {
if(e.target.status==404)
{
alert("The song was not found!");
}
});
POST requests from AJAX
To send POST requests from AJAX, you need to create a
FormData object and then append each item of POST data
to it. The example below will create three items of POST data,
flightnumber, origin and destination, and send them
to newflight.php. Note how the FormData object is supplied as a
parameter to the ajaxConnection.send() function. In the PHP script, we'd then
read the data using $_POST["flightnumber"],
$_POST["origin"] and $_POST["destination"].
var ajaxConnection = new XMLHttpRequest();
var data = new FormData();
data.append("flightnumber", "SA177");
data.append("origin", "London");
data.append("destination", "Denver");
ajaxConnection.addEventListener("load", { ... callback function ... } );
ajaxConnection.open("POST", "newflight.php");
ajaxConnection.send(data);
Querying and Manipulating HTML documents using the DOM
In previous units, you've seen the innerHTML property which can be used to read, or change, the text within
an HTML element. You've also looked at simple use of the
Document Object Model (DOM) to access elements on a web page -
specifically the use of getElementById() to access a specific
page element using its ID.
However that's just the start: the DOM offers a whole range of ways to read and manipulate HTML pages or
XML data. To understand how you can use DOM for document manipulation, you must understand the concept of
nodes, which we will discuss below.
The examples below use the DOM to manipulate HTML documents, however, more generally,
the DOM is used for accessing and manipulating XML documents. An HTML web page is a particular, specific type of XML document. So, as well as using the DOM to query and manipulate web pages,
we can use it in a more general sense to query and manipulate XML. In AJAX, this latter use of the DOM is used
extensively.
The Concept of Nodes
- Part of the W3C Document Object Model (DOM)
- A systematic way to navigate and manipulate the content of an HTML or XML document, not
only the elements, but also the text within them
- An HTML or XML document consists of a series of hierarchical nodes
- Each element (e.g p, div, or em
in HTML; or a custom tag in XML) is treated as a node
- However it's not just the elements themselves:
the text within each element is also treated as a special kind of node, a text node
- The nodes are a nested, hierarchical structure
- An element within an element is a child node of that element
Example of Nodes Terminology
<body>
<p> Welcome to the <em>wonderful!</em> world of dynamic text!</p>
</body>
The paragraph is a child node of the body
The paragraph contains three of its own child nodes:
- The text: "Welcome to the"
- The em element
- The text: "world of dynamic text!"
The em itself contains one child node:
- The text: "wonderful!"
- This is a child of the em not the
p
Nodes Hierarchy Diagram: Tree view
<body>
<p> Welcome to the <em>wonderful!</em> world of dynamic text!</p>
</body>
Querying the Node Structure of a Document
- document.body.childNodes is an array of all
child nodes of the body
- i.e. all elements and text within the page
- node.childNodes is an array of all child nodes of
a given node (i.e. element)
- node.nodeName gives the name of a node
- i.e. em, p, song, artist, etc; or #text for text nodes
- node.nodeValue gives the text within a text node
(only)
- It has no meaning for page elements
Example
Adding a new node
- One of the keys to dynamic text
- document.createElement() allows us to create a brand
new element!
- document.createTextNode() allows us to create a brand new
text node
- Having created an element we can then populate it with text nodes
and child elements
- ... and finally add it to either:
- The body
- Another element, which will act as its parent
Example
Replacing an existing node
- The other key to dynamic text
- We can replace a whole paragraph of text with another!!!
- Steps:
- Find the element to replace
- Create a new element (as per the previous example)
- Call replaceChild() to replace the old
node with the new
Example
Getting all elements of a particular type
- It can make it easier to find a page element if we can collect
together all elements of a particular type (e.g. all paragraphs)
- document.getElementsByTagName() allows us to do this
- This gives us an array of all elements of a particular
type, which we can then index
Example
Removing nodes
Other useful features of the DOM
(Source:
Quirksmode, a very useful reference site for JavaScript
and the DOM)
- childNodes property - an array of all the child nodes of a given node.e.g.:
parent.childNodes.forEach ( { childNode =>
alert(childNode.nodeName); // show name of the node
});
- firstChild and lastChild properties - the first and last
child node of a given node. e.g.
// create a child node of the parent, assume that 'parent' has no children yet
var p = document.createElement("p");
parent.appendChild(p);
// create a text node
var textNode = document.createTextNode("some text");
// append it to the first child of the node 'parent', which will be the
// paragraph we created above. In other words 'p' and 'parent.firstChild'
// are the same in this case.
parent.firstChild.appendChild(textNode);
- parentNode property - the parent node of a given child node. So for the code:
var p = document.createElement("p");
parent.appendChild(p);
the parentNode of p would be parent.
- previousSibling and nextSibling - find the sibling nodes
(siblings = Geschwister in German). Imagine that a given parent node has three child
nodes, 0, 1 and 2. The previousSibling of child node 1 would be child node 0. The
nextSibling of child node 1 would be child node 2.
- insertBefore method - inserts a child node into a parent node at an arbitrary
position, e.g:
parentNode.insertBefore(newNode,parentNode.childNodes[3]);
This code would insert newNode before child node 3 of the parent.
- document.querySelector()
and document.querySelectorAll() give the first
element, and all elements, which match a CSS selector respectively, eg
document.querySelectorAll(".important")
will give an array of all elements with a class of important.
More advanced exercise -
Using the DOM to add Order Functionality to your
HitTastic! AJAX site
Do not attempt this unless you have done all exercises from
the first AJAX topic last time, and are comfortable with the
material on DOM nodes from this week. Also ensure that you are completely up to date with Sessions 1 to 4.
Inspecting elements
The inspect functionality in the browser will help you see
what's going on - try it as you do this exercise - right click on an
element. Inspecting helps you to see the actual current DOM structure of the
page - including any dynamically-created elements.
Questions
You are going to extend the functionality of your
AJAX-based HT-Tracks website so that the user is able to order physical copies
(CD, vinyl) of songs.
- Extend your Slim web service to feature an "order" route
(e.g. /song/{id}/order). This should reduce the quantity of that
song by one, and add an entry into the "orders" table containing the song
ID and a quantity of 1.
- Make a copy, in a new file, of your existing HT-Tracks
AJAX page. Delete all the existing code in the foreach loop.
- Add code to your AJAX callback function
so that it creates a DOM paragraph (p) element
for each hit returned.
- Within this p, create:
- a text node containing the details of the song, and
- a button node (i.e. an
"input" with a type of "button") allowing the user to order
the song. Add both to the paragraph node using appendChild().
To make sure that the button is an input with a type of "button", you
need to set the "type" property to "button", i.e. if btn is your
button, then you can use: btn.type = "button";
- Specify the text on the button by setting the "value" property:
btn.value="Order!";
- Set up a "click" event handler for the button. as an arrow function, e.g:
btn.addEventListener ("click", e =>
{
// fill in your arrow function here.
// It should call your "order" web service route, passing in the ID of the current song.
}
);
This will set the "click" handler of the button to be an arrow function.
- Add each paragraph to the results div using appendChild().
- You can now complete the arrow function which runs when the user clicks
the button. Use AJAX to call the "order" route of your web service.
You will need to call the web service using a method of POST (see above),
though in this case you will not need to send any FormData.
-
The callback to the AJAX order request should
display a "Successfully ordered" message, e.g. in an alert box.
- Add error handling to the "order" route of your
web service so that if the required
song is out of stock, a 404 is returned.
- Alter your AJAX front end to check for a 404 from the "order"
route of your web service. In the callback, display a different message
depending on whether the song was bought successfully or not.
- More advanced: Alter your "order" route to be able
to order more than one copy of the song (receive the quantity as POST
data). Update your AJAX front end to allow the user to enter the quantity,
and send the entered quantity to the web service.
As this is an advanced question, I am leaving it up to you to figure
out how to do this but if you are really stuck, I will give you some
hints.