Up until now all your scripts have run on the server however you can run JavaScript in the web browser as well. The good news is that you can use the same JavaScript syntax in both however there are a few key differences:
- You can't connect to any resources on the server such as databases (however you can use the database built into the web browser).
- One of the most important applications is interacting with the browser by using the
document
object.
Unlike in server-side scripting, we need to load the JavaScript file into the web browser. To do this we create a link from the HTML file to the JavaScript file and allow the browser to load this over HTTP. For this to work the JS file needs to be publicly accessible by the browser. This is why it is placed in the Express public directory on the server.
- Locate the
13_client/dom/html/dom.html
file. - Notice the
<script>
element after the closing<body>
element.- It is important that we run the script after the html body has loaded because we will be attaching events to it and modifying it.
The key to interacting with the web page is the window
object which is a top-level object in Client Side JavaScript. It represents a window or a frame (within a frameset). The window
object is a top-level object and contains other objects such as document
, history
etc. within it.
The window.document
object contains the entire web page. Start by opening the 13_client/dom/index.js
script (after installing the necessary modules) and viewing the website using Google Chrome.
Since the JavaScript code is running in the web browser, we need to use the Chrome Developer Tools to see what us happening. Open the Chrome Developer Tools and locate the Console tab. Open the dom/public/cs/simple_dom.js
file.
- In the console you will see three messages:
- The first thing is a message
window 'load' event triggered
. - The second item is the
document
object. This contains the entire html page as a JavaScript object. Use the arrow to expand the object, notice this is the html web page (compare this to the contents of thedom/html/dom.html
file). - Finally the browser window width is displayed:
- Try resizing the browser window, what is displayed in the browser console?
- How could you use this information to adjust the layout to suit different devices (adaptive layout)?
- Take a look at the
simple_dom.js
file, can you identify where these messages are coming from? - The
document
object contains theaddEventListener()
function which takes two parameters:- the event to respond to (supplied as a string literal.
- an anonymous function callback that is triggered by the event.
- There are a large number of events that can be used to trigger your code. Check the full list for more information.
- The first thing is a message
- Sections of the DOM can be extracted by passing a CSS Selector to the
querySelector()
property. In thesimple_dom.js
script we extract part of the DOM and store it in theemailField
variable.- This DOM fragments also have event handlers. In this case we are using the
onkeypress()
function. This is triggered when the user selects the field and presses a key. - Select the email field and enter your email address.
- Each time you press a key, notice that the console prints the message
updating email
. - The html form field has a
value
property which returns the contents of the text field. - Finally we use a CSS selector to retrieve the DOM representing the paragraph tag.
- We use the
.innerHTML
property to change the text inside this element. - Can you see a potential issue (compare what us displayed with what is in the email field).
- Replace the
onclick()
event with thekeydown()
event, does this make any difference? - Replace it with
keyup()
, does this help? Can you figure out why?
- Replace the
- This DOM fragments also have event handlers. In this case we are using the
- Another common way to select a DOM element is to reference it by its ID by using the
getElementById()
function.- Try completing the form and clicking on the Save button.
- If you examine the browser console you will see a lot of information printed.
- The script updates the heading and paragraphs.
- Create a paragraph for each field in the form and assign a unique ID attribute to each.
- When the form is submitted, display each item in the form in a different paragraph.
- Add an
onmouseover()
event to the heading that changes the title to displayHello Mouse!
.- What happens when you move the mouse away from the heading?
- Add an
onmouseout()
event to fix this... - Remember that we had to import the script after the page had loaded (we placed the script tag after the
<body>
element).- Lets move the
<script>
element to inside the<head>
element in the html. - How can we ensure the script only runs after the html has loaded? (hint: think about the event handlers triggered by the
window
object).
- Lets move the
In the previous example we added event handlers to the existing DOM. In this section you will learn how to make changes to the DOM and thus change the html displayed in the web browser. Start the server, open the web page in the browser and open the lists/public/js/building_lists.js
script.
- At the top of the script we create a new
XMLHTTPRequest()
object. This is used to make HTTP GET requests and can be used to load online resources.- Normally we use this asynchronously (we will cover this in the next section).
- In this example we make the call synchronously.
- We now use the
open()
function to retrieve the data:- The first parameter is the method to be used.
- The second is the URL of the data (in this case it is a file on the same server).
- The third parameter defines whether the request is async (not in this case).
- Now we send this request to the retrieve the data.
- The
responseText
property contains the text string that is returned by the HTTP request.- This is a JSON string and needs to be converted to a JS object using
JSON.parse()
.
- This is a JSON string and needs to be converted to a JS object using
Now we have the data we need to create a DOM object and insert this into the page DOM:
- We use the
createElement()
function to create a new DOM element (remember this is a JS object). - We loop through the array of data returned from the XMLHTTPRequest and use this to build additional elements.
- These are added to the added a child elements using the
appendChild()
function.
- These are added to the added a child elements using the
- Finally the entire, complex DOM object is appended to the
window.document
object and so appears in the browser. - Locate the Elements tab in the Chrome developer tools:
- Notice that the DOM now shows the list.
- Replace the
for
loop with afor-of
loop. - Create a
<div>
element under the list. - Loop through the data to build a 3 column table:
- The first column should contain the title.
- Column 2 should display the ISBN number.
- The final column the year.
- Insert this table into the
<div>
element.
In the previous example we loaded a JSON string from a local file using a synchronous XMLHTTPRequest. Whilst this worked in this case, normally the request retrieves data from a remote server and this can introduce a large latency into the process, potentially freezing the UI until completed. To prevent this you should always make asynchronous HTTP requests which can be handled by non-UI threads. This is what you will be doing in this exercise.
Start by running the web server in the 13_client/books/
directory and opening the web page. Locate and open the books/public/js/book_review.js
file.
- Try entering a search term in the text field. What happens?
- Study the
book_review.js
script to see if you can understand how this happens.
- Study the
- There is an
onkeyup()
event handler attached to the text field that is triggered as each key is released on your keyboard. - This triggers the
search()
function, passing the string entered in the text field. - We create an
XMLHTTPRequest
object:- We use it to make a
GET
request to the Google Books API. The third parameter flags this as an asynchronous request.
- We use it to make a
- The function prototype in the
onload
property is triggered if the request succeeds:- The
readyState
property contains a number between 0 and 4 with 4 indicating the data has been returned. - The
status
property contains the HTTP Status (eg. 200 OK). If this is not 200 we flag an error. - The data we have returned is added to the DOM.
- The
- The function prototype in the
onerror
property is triggered if an error occurs.
- The
onkeyup()
event currently uses thesearchPrep()
function. Rewrite this using an anonymous function. - Replace the
for
loop with afor-of
loop. - Rewrite the
search()
function to use a second callback parameter. This should have two parameters:- The first parameter should return an error (or
null
if no error). - The second parameter should return a DOM table object that can be inserted into the page DOM.
- The first parameter should return an error (or
Only supports strings. Need to convert objects to json strings. Make sure you have opened the Application tab in the Chrome Developer Tools pane. Locate the Storage section in the left pane.
- Close the browser (completely) and reopen. Is the data still there?
- Local storage: Stores data with no expiration date. The data will be available even when the browser/ browsing tab is closed or reopened.
- Change the
localstorage
object for thesessionstorage
object. - Close the browser (completely) and reopen. Is the data still there?
- Session storage: Stores data for one session. Data persisted will be cleared as soon as the user closes the browser.
// TODO: complete section
Lets put all this together and build a simple login system. Locate the 13_client/login/
directory, install the dependencies and start the Express server. Open the Chrome browser and open the Chrome Developer tools on the Network tab.
- Open your website's base URL,
/
.- If you look at the URL you will see that it has changed to
/login
. - This is caused by the original page redirecting the browser to this page.
- If you look at the URL you will see that it has changed to
- In the Network tab you will see the files that have been loaded into the browser, this includes the html page and a javascript file.
- Select the
login
file as shown below and select the Headers tab. This will show you the page request and response headers. - Notice that the HTTP Status Code is 304 rather than 200 which indicates it was the target of a redirect. This makes sense.
- Select the
- Switch to the Console tab, enter a random username and password and click on the button.
- Notice that the browser has created a token string, this is formed by combining the supplied username and password with a
:
separator (such asmyusername:mypassword
) and then Base64 encoding it). - The client then makes a
GET
request to the/checkauth
route on your server. - The token is passed in a special request header called
Authorization
.- The value passed comprises the string
Basic
followed by a space and then the token. - This is an example of Basic Access Authentication.
- The value passed comprises the string
- The server responds with a status code of
401 UNAUTHORIZED
which is not surprising given we suppplied random login details! - Notice all this happened without the page being reloaded, this is because the HTTP request was made by the
XMLHTTPRequest
object, not by the browser itself.
- Notice that the browser has created a token string, this is formed by combining the supplied username and password with a
- Now enter a valid login, use
jdoe
as the username andp455w0rd
as the password. 1.
// TODO:
// TODO: complete section
// TODO: complete section
Handled with network events
// thanks to David Walsh
// https://davidwalsh.name/detecting-online
window.addEventListener('online', () => {
console.log('you are ONLINE')
})
window.addEventListener('offline', () => {
console.log('you are OFFLINE')
})
If you are interested in looking into client-side javascript in more detail, find out about the following:
- async
- defer