Lecture 5 Javascript
Lecture 5 Javascript
Introduction
JavaScript
Events
Variables
querySelector
DOM Manipulation
o JavaScript Console
o Arrow Functions
o TODO List
Intervals
Local Storage
APIs
o JavaScript Objects
o Currency Exchange
Introduction
So far, we’ve discussed how to build simple web pages using
HTML and CSS, and how to use Git and GitHub in order to
keep track of changes to our code and collaborate with others.
We also familiarized ourselves with the Python programming
language, started using Django to create web applications,
and learned how to use Django models to store information in
our sites.
Today, we’ll introduce a new programming language:
JavaScript.
JavaScript
Let’s begin by revisiting a diagram from a couple of lectures ago:
alert('Hello, world!');
The alert function in JavaScript displays a message to the user which
they can then dismiss. To show where this would fit into an actual
HTML document, here’s an example of a simple page with some
JavaScript:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
<script>
alert('Hello, world!');
</script>
</head>
<body>
<h1>Hello!</h1>
</body>
</html>
Events
One feature of JavaScript that makes it helpful for web programming
is that it supports Event-Driven Programming.
Event-Driven Programming is a programming paradigm that centers
around the detection of events, and actions that should be taken
when an event is detected.
function hello() {
alert('Hello, world!')
}
Now, let’s work on running this function whenever a button is
clicked. To do this, we’ll create an HTML button in our page with
an onclick attribute, which gives the browser instructions for what
should happen when the button is clicked:
Variables
JavaScript is a programming language just like Python, C, or any
other language you’ve worked with before, meaning it has many of
the same features as other languages including variables. There are
three keywords we can use to assign values in JavaScript:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Count</title>
<script>
let counter = 0;
function count() {
counter++;
alert(counter);
}
</script>
</head>
<body>
<h1>Hello!</h1>
<button onclick="count()">Count</button>
</body>
</html>
querySelector
In addition to allowing us to display messages through alerts,
JavaScript also allows us to change elements on the page. In order
to do this, we must first introduce a function
called document.querySelector. This function searches for and returns
elements of the DOM. For example, we would use:
heading.innerHTML = `Goodbye!`;
Just as in Python, we can also take advantage of conditions in
JavaScript. For example, let’s say rather than always changing our
header to Goodbye!, we wish to toggle back and forth
between Hello! and Goodbye!. Our page might then look something
like the one below. Notice that in JavaScript, we use === as a
stronger comparison between two items which also checks that the
objects are of the same type. We typically want to
use === whenever possible.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Count</title>
<script>
function hello() {
const header = document.querySelector('h1');
if (header.innerHTML === 'Hello!') {
header.innerHTML = 'Goodbye!';
}
else {
header.innerHTML = 'Hello!';
}
}
</script>
</head>
<body>
<h1>Hello!</h1>
<button onclick="hello()">Click Here</button>
</body>
</html>
DOM Manipulation
Let’s use this idea of DOM manipulation to improve our counter
page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Count</title>
<script>
let counter = 0;
function count() {
counter++;
document.querySelector('h1').innerHTML = counter;
}
</script>
</head>
<body>
<h1>0</h1>
<button onclick="count()">Count</button>
</body>
</html>
We can make this page even more interesting by displaying an alert
every time the counter gets to a multiple of ten. In this alert, we’ll
want to format a string to customize the message, which in
JavaScript we can do using template literals. Template literals
requre that there are backticks (`) around the entire expression and
a $ and curly braces around any substitutions. For example, let’s
change our count function
function count() {
counter++;
document.querySelector('h1').innerHTML = counter;
if (counter % 10 === 0) {
alert(`Count is now ${counter}`)
}
}
Now, let’s look at some ways in which we can improve the design of
this page. First, just as we try to avoid in-line styling with CSS, we
want to avoid in-line JavaScript as much as possible. We can do this
in our counter example by adding a line of script that changes
the onclick attribute of a button on the page, and removing
the onclick attribute from within the button tag.
document.querySelector('button').onclick = count;
One thing to notice about what we’ve just done is that we’re not
calling the count function by adding parentheses afterward, but
instead just naming the function. This specifies that we only wish to
call this function when the button is clicked. This works because, like
Python, JavaScript supports functional programming, so functions
can be treated as values themselves.
document.addEventListener('DOMContentLoaded', function() {
// Some code here
});
In the example above, we’ve used an anonymous function, which is
a function that is never given a name. Putting all of this together,
our JavaScript now looks like this:
let counter = 0;
function count() {
counter++;
document.querySelector('h1').innerHTML = counter;
if (counter % 10 === 0) {
alert(`Count is now ${counter}`)
}
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('button').onclick = count;
});
Another way that we can improve our design is by moving our
JavaScript into a separate file. The way we do this is very similar to
how we put our CSS in a separate file for styling:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Count</title>
<script src="counter.js"></script>
</head>
<body>
<h1>0</h1>
<button>Count</button>
</body>
</html>
And a file called counter.js that looks like this:
let counter = 0;
function count() {
counter++;
document.querySelector('h1').innerHTML = counter;
if (counter % 10 === 0) {
alert(`Count is now ${counter}`)
}
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('button').onclick = count;
});
Having JavaScript in a separate file is useful for a number of
reasons:
We use the autofocus field in the name input to indicate that the
cursor should be set inside that input as soon as the page is
loaded.
We use #name inside of document.querySelector to find an
element with an id of name. We can use all the same selectors
in this function as we could in CSS.
We use the value attribute of an input field to find what is
currently typed in.
We can do more than just add HTML to our page using JavaScript,
we can also change the styling of a page! In the page below, we use
buttons to change the color of our heading.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Colors</title>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('button').forEach(function(button) {
button.onclick = function() {
document.querySelector("#hello").style.color =
button.dataset.color;
}
});
});
</script>
</head>
<body>
<h1 id="hello">Hello</h1>
<button data-color="red">Red</button>
<button data-color="blue">Blue</button>
<button data-color="green">Green</button>
</body>
</html>
Some notes on the page above:
The console is a useful tool for testing out small bits of code and
debugging. You can write and run JavaScript code in the console,
which can be found by inspecting element in your web browser and
then clicking console. (The exact process may change frome browser
to browser.) One useful tool for debugging is printing to the console,
which you can do using the console.log function. For example, in
the colors.html page above, I can add the following line:
console.log(document.querySelectorAll('button'));
Which gives us this in the console:
Arrow Functions
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('button').forEach(button => {
button.onclick = () => {
document.querySelector("#hello").style.color = button.dataset.color;
}
});
});
We can also have named functions that use arrows, as in this
rewriting of the count function:
count = () => {
counter++;
document.querySelector('h1').innerHTML = counter;
if (counter % 10 === 0) {
alert(`Count is now ${counter}`)
}
}
To get an idea about some other events we can use, let’s see how
we can implement our color switcher using a dropdown menu
instead of three separate buttons. We can detect changes in
a select element using the onchange attribute. In JavaScript, this is a
keyword that changes based on the context in which it’s used. In
the case of an event handler, this refers to the object that triggered
the event.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Colors</title>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('select').onchange = function() {
document.querySelector('#hello').style.color = this.value;
}
});
</script>
</head>
<body>
<h1 id="hello">Hello</h1>
<select>
<option value="black">Black</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
</body>
</html>
onclick
onmouseover
onkeydown
onkeyup
onload
onblur
…
TODO List
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tasks</title>
<script src="tasks.js"></script>
</head>
<body>
<h1>Tasks</h1>
<ul id="tasks"></ul>
<form>
<input id="task" placeholder = "New Task" type="text">
<input id="submit" type="submit">
</form>
</body>
</html>
Now, here’s our code which we can keep in tasks.js. A few notes on
what you’ll see below:
// Create a list item for the new task and add the task to it
const li = document.createElement('li');
li.innerHTML = task;
Intervals
In addition to specifying that functions run when an event is
triggered, we can also set functions to run after a set amount of
time. For example, let’s return to our counter page’s script, and add
an interval so even if the user doesn’t click anything, the counter
increments every second. To do this, we use
the setInterval function, which takes as argument a function to be
run, and a time (in milliseconds) between function runs.
let counter = 0;
function count() {
counter++;
document.querySelector('h1').innerHTML = counter;
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('button').onclick = count;
setInterval(count, 1000);
});
Local Storage
One thing to notice about all of our sites so far is that every time we
reload the page, all of our information is lost. The heading color
goes back to black, the counter goes back to 0, and all of the tasks
are erased. Sometimes this is what we intend, but other time’s we’ll
want to be able to store information that we can use when a user
returns to the site.
function count() {
// Retrieve counter value from local storage
let counter = localStorage.getItem('counter');
// update counter
counter++;
document.querySelector('h1').innerHTML = counter;
document.addEventListener('DOMContentLoaded', function() {
// Set heading to the current value inside local storage
document.querySelector('h1').innerHTML = localStorage.getItem('counter');
document.querySelector('button').onclick = count;
});
APIs
JavaScript Objects
A JavaScript Object is very similar to a Python dictionary, as it allows
us to store key-value pairs. For example, I could create a JavaScript
Object representing Harry Potter:
let person = {
first: 'Harry',
last: 'Potter'
};
I can then access or change parts of this object using either bracket
or dot notation:
{
"origin": "New York",
"destination": "London",
"duration": 415
}
The values within a JSON do not have to just be strings and numbers
as in the example above. We can also store lists, or even other
JavaScript Objects:
{
"origin": {
"city": "New York",
"code": "JFK"
},
"destination": {
"city": "London",
"code": "LHR"
},
"duration": 415
}
Currency Exchange
<!DOCTYPE html>
<html lang="en">
<head>
<title>Currency Exchange</title>
<script src="currency.js"></script>
</head>
<body></body>
</html>
Now, we’ll use something called AJAX, or Asynchronous JavaScript
And XML, which allows us to access information from external pages
even after our page has loaded. In order to do this, we’ll use
the fetch function which will allow us to send an HTTP request.
The fetch function returns a promise. We won’t talk about the details
of what a promise is here, but we can think of it as a value that will
come through at some point, but not necessarily right away. We
deal with promises by giving them a .then attribute describing what
should be done when we get a response. The code snippet below will
log our response to the console.
document.addEventListener('DOMContentLoaded', function() {
// Send a GET request to the URL
fetch('https://api.exchangeratesapi.io/latest?base=USD')
// Put response into json form
.then(response => response.json())
.then(data => {
// Log data to the console
console.log(data);
});
});
One important point about the above code is that the argument
of .then is always a function. Although it seems we are creating the
variables response and and data, those variables are just the
parameters of two anonymous functions.
document.addEventListener('DOMContentLoaded', function() {
// Send a GET request to the URL
fetch('https://api.exchangeratesapi.io/latest?base=USD')
// Put response into json form
.then(response => response.json())
.then(data => {
<!DOCTYPE html>
<html lang="en">
<head>
<title>Currency Exchange</title>
<script src="currency.js"></script>
</head>
<body>
<form>
<input id="currency" placeholder="Currency" type="text">
<input type="submit" value="Convert">
</form>
<div id="result"></div>
</body>
</html>
Now, we’ll make some changes to our JavaScript so it only changes
when the form is submitted, and so it takes into account the user’s
input. We’ll also add some error checking here:
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('form').onsubmit = function() {
That’s all for this lecture! Next time, we’ll work on using JavaScript
to create even more engaging user interfaces!