This is lab explores even more crucial functionalities of javascript, some of which you won't see in any other language
Just as in lab 1 follow the link below to open codesandbox. Fork the project then sign in.
These functions allow us to get immediate user input blocking all other actions on the page.
If you run the workspace at this point you shall see the following:
Notice the mark up on the page
Let's add the following event handlers to the script tag of the page, press the button and observe the output.
index.html
<script>
function alertFun(){
alert("Hello !");
}
function confirmFun(){
let didConfirm = confirm("Are you sure about that?");
if(didConfirm){
alert("You confirmed :)");
}else{
alert("You did not confirm :(");
}
}
function promptFun(){
let val = prompt("What is your name?");
alert(`You've entered: ${val}`);
}
</script>
The Browser Object Model is a browser-specific convention referring to all the objects exposed by the web browser. Web APIs which allow us to tap into the wider features of the web forms part of the BOM in addition to the Document Object Model (DOM) accessible via the document
object.
The window
object represents the BOM and lets us access other aspects of the browser such as the document
or location
via a property of window e.g. window.location
or call them directly as a global object e.g. location.
Execute the following and observe the output.
//get client data using navigator
console.log(window.navigator.cookieEnabled);
console.log(window.navigator.onLine);
console.log(navigator.appVersion);
console.log(navigator.userAgent)
console.log(navigator.platform);
//get window metadata using window
console.log(window.location.href);//get full url
console.log(window.location.protocol);
console.log(window.location.hostname);
function redirect(url){
window.location.assign(url);//redirects the page to another url
}
window.onload = function(event){
console.log("Page has loaded");
//do other javascript stuff here
}
Using window.onload is a pattern which can benefit the performance of the application. It can be used to pause your javascript execution until the page has fully loaded.
//Global variables declared with var are automatically added to the window object
var bob = 'bob';
const sally = 'sally';
console.log(window.bob === bob);//true
console.log(window.sally === sally);//false
var document = 'hello this is my variable';
console.log(document);//give the DOM object instead
console.log(window.document === document)//true because declaration ignored
...
<script src="A.js"></script>
<script src="B.js"></script>
</body>
The Document Object Model (DOM) refers to the hierarchy of HTML elements of the page. It is part of the BOM and can be accessed via window.document
or directly via the document
object.
One of the methods we can use to access an element on the DOM is the querySelector()
method. document.querySelector()
will return the element that matches any valid css selector passed to it.
For example, we can access the span element on the page with the following.
const result = document.querySelector("#result");
console.log(result);
If the following is executed in the browser, the span element with the id result will be logged to the console.
However the result variable is not an html string but rather a HTML Element object.
We can use the innerHTML property on HTML elements to change its html content on the page. We can also inject children elements by assigning strings written in html. For example the following code:
will inject the h2 with content "My span" to the page.
We can use the properties of HTML Element objects to retrieve and manipulate the element's styles, attributes and classes using javascript.
console.log(result.style);//shows all the styles of the object
result.style.backgroundColor = 'lightgrey';
console.log(result.getAttribute('id'));//result
result.removeAttribute('id');//removes an attribute
result.setAttribute('id','result');//sets an attribute
result.classList.add('content');//adds a class
result.classList.remove('content');//removes a class
Write and execute the following in the script tag.
const result = document.querySelector('#result');
result.innerHTML = '<h2>My Span</h2>';
result.style.color = 'blue';
As shown in section 1, we can bind javascript functions to DOM events using onevent attributes such as onclick
. The code below will call the function myFun() when the button is clicked. ie a click event occurs on the button.
<input id="myBtn" type="button" onclick="myFun()" value="click me"/>
However we can use the addEventListener() method on HTML elements to attach event handlers at runtime.
Add the above code to the page then pass the mouse over the button and click the button several times.
<input id="myBtn" type="button" onclick="myFun()" value="click me"/>
<script>
function myFun(){
alert("hello");
}
function myFun2(){
console.log("myFun2 called");
}
//receives the event parameter from addEventListener high order function
function logEventType(event){
console.log(event.type);
}
let myBtn = document.querySelector("#myBtn");
//attach myFun2 in addition to myFun to the click event of myBtn
myBtn.addEventListener("click", myFun2);
//There are other events such as mouseover and mouseout
//Any callback passed to addeventListener receives an event object
myBtn.addEventListener("mouseover", logEventType);//logs 'mouseover'
myBtn.addEventListener("mouseout", logEventType);// logs 'mouseout'
//these events will fire when the mouse pointer hovers/passes over the button
</script>
You should get an alert on every click and in the console tab that each mouseover and mouseout event has been logged due to the event listeners attached to the button.
Write markup and code in index.html to do the following;
Javascript objects are always references. This means if a function receives an object as a parameter, it is really receiving a reference to the object.
When a function performs operations on an object those operations are applied to wherever the object is originally declared. Because of references, changes made to an object are applied to anywhere else the object is used.
let bob = {
name: 'Bob',
balance: 10
}
function add10(a){
a.balance+= 10; // a is a pointer/reference to myObj
}
console.log(bob.balance);//10
add10(bob);
console.log(bob.balance);//20
As a result of this, when you assign a variable to an existing object, then both variables are the same reference.
If you want to make a copy to an object to avoid this can accomplish that with the following using Object.assign
let obj2 = myobj;
//obj2 and myobj are the same variable
obj2.name = "Shelly";
console.log(myobj.name);//Shelly
//if you need to create a new object and copy its values use Object.assign
let obj3 = {};
Object.assign(obj3, myobj);
obj3.name = 'Smith';
console.log(myobj.name, obj3.name);//Shelly, Smith
This is said to perform a shallow copy because any nested objects in myobj will have references to them in copy. It's best to avoid copying in the first place if you want to be able to create objects of a particular structure then implement a constructor function.
Execute the following and observe how changing ob2 affects myobj
let obj2 = myobj; //obj2 and myobj are the same variable obj2.name = "Shelly"; console.log(myobj.name);//Shelly //if you need to create a new object and copy its values use Object.assign let obj3 = {}; Object.assign(obj3, myobj); obj3.name = 'Smith'; console.log(myobj.name, obj3.name);//Shelly, Smith
The same also applies to arrays.
Update Index html with the following markup. Notice the table body is given an id as that is the target for rendering data on the Document Object Model (DOM).
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<title>Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<nav>
<div class="nav-wrapper teal" style="padding-left: 10px;">
<a href="#!" class="brand-logo">Ajax Demo</a>
</div>
</nav>
<main class="row" style="padding: 40px">
<table>
<thead>
<tr>
<th>#</th>
<th>name</th>
<th>Type</th>
<th>website</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
</main>
<script>
</script>
</body>
</html>
We have utilized 3rd party css from https://materializecss.com/. To add some pleasing styling to our site.
The result is an empty table. Next we would want to write some javascript code to fetch the data then write it out to the table. This can be done in two functions; loadData() and writeTable().
First let's start with writeTable(). This should be familiar from the previous lab, writing html elements for every element in an array.
For every object in an array we want to display the id, name, brewery_type and website_url properties in the columns of the table. For example
// if a record looks like.
let record = {
id: 1,
name: "bob's"
brewery_type: "fire",
website_url:"https://bobs.com"
}
//*note the use of backticks
//using string interpolation
//a row in the table would look like;
Let row = `<tr>
<td>${record.id}</td>
<td>${record.name}</td>
<td>${record.brewery_type}</td>
<td>${recoard.website_url}</td>
</tr>`;
We want to do this for every record in an array so the draw table function would do this in a for loop.
Implement drawTable() in the script tag of
index.html.
function drawTable(records){
let result = document.querySelector('#result');
//add html code inside of result
let html = '';// create html string
for(let record of records){
//build html string
html += `<tr>
<td>${record.id}</td>
<td>${record.name}</td>
<td>${record.brewery_type}</td>
<td>${record.website_url}</td>
</tr>`;
}
result.innerHTML = html;//add html string to DOM
}
//testing the function
drawTable([{
id: 1,
name: "bob's",
brewery_type: "fire",
website_url:"https://bobs.com"
}]);
The result should look like
Now we have a function that can present elements of the array to the DOM, the next step is the fetch the data.
The core functionality that makes client side web application dynamic is Asynchronous Javascript and XML (AJAX). Whereby we use javascript code to allow the browser to make an XMLHttpRequest. When a response is received by the server we output the result to the page.
Visit the link https://api.openbrewerydb.org/breweries/search?query=harry
and view the result. You should see the following.
That url is said to be an Application Programming Interface (API) endpoint. What it simply means is that the server which resides at that address is programmed to only return data as opposed to html pages. Parameters can be specified in the url to affect the data being returned.
In this case the query parameter "query" is given the value "harry" hence brewery data is returned for breweries which partially match with "harry".
To make the data look more presentable we can have Javascript request this data and write it out into our web application.
Implement the following function to fetch the data and pass it on to drawTable()
//function MUST be declared async
async function getData(url){
try{
let response = await fetch(url);//1. Send http request and get response
let result = await response.json();//2. Get data from response
drawTable(result);// 3. Do something with the data
}catch(e){
console.log(e);//catch and log any errors
}
}
getData("https://api.openbrewerydb.org/breweries/search?query=harry")
The data should now be automatically rendered with the data it retrieved from performing an HTTP request.
The functions drawTable() and getData() are VERY IMPORTANT implementations which you would rely on for this course. Be sure to understand the pattern to apply it in the assignment, exams and otherwise.
In the last section, brewery data is requested as soon as the page is loaded. Often in web applications, we cannot anticipate the exact data needed by the user. Data may be requested dynamically with respect to some parameters supplied through user input.
You can view this example running at the following workspace.
Take a look at the updated getData() function.
async function getData(url, renderFun){
try{
let response = await fetch(url);//1. Send http request and get response
let result = await response.json();//2. Get data from response
renderFun(result);// 3. Do something with the data
}catch(e){
console.log(e);//catch and log any errors
}
}
//get all brewery data on page load and render a table
getData("https://api.openbrewerydb.org/breweries", drawTable)
Note the updated drawTable() function
function drawTable(records){
let result = document.querySelector('#result');
//add html code inside of result
let html = '';// create html string
for(let record of records){
//build html string
html += `<tr id="${record.id}">
<td>${record.id}</td>
<td>${record.name}</td>
<td>${record.brewery_type}</td>
<td><a href="#${record.id}" onclick="getData('https://api.openbrewerydb.org/breweries/${record.id}', drawDetails)" >View More Details</a></td>
</tr>`;
}
result.innerHTML = html;//add html string to DOM
}
This concludes this lab on AJAX which you shall rely on heavily for this course, with the last two functions being the very core.