Page 3: HTML and JavaScript

CS559 Spring 2021 Sample Solution - Workbook 1

Written by CS559 course staff

You can try out the example solutions here. The interesting code is on page 6.

On the Previous page we made simple web pages, here we’ll see how to put JavaScript programs on them.

JavaScript can be tricky to learn because not only must you learn a new language, but usually the programming happens in the context of a web page in a web browser.

The Javascript in CS559 has all sorts of resources about JavaScript, including the rationale for why we are using it in class. If you are already a JavaScript expert, that page will tell you some rules for how we use JavaScript in class. If you are just learning JavaScript, the page has links to some good tutorials. You will also be able to figure a lot out from the examples we provide in the workbooks.

The goal of this page is to help you understand where JavaScript programs go, when they run, and how they work with the workbooks. If anything is too unfamiliar, you might want to review some of the Javascript resources on the Javascript in CS559 page.

Box 1: Where does my code go?

Here is a miniature web page 01-03-01.html. It’s boring but it does have a little program in it.

The output of this program, from the console.log function, will be in the “console” of your web browser. Now is a good time to look for it (it should say “Box 1’s initial message to the console”, but you’ll also see the output from box 3 and 4). You might try opening the box as a separate web page (here’s a link). See what the console shows for this.

If you’re not familiar with the console, now is a good time to find it and try it out. You can type JavaScript into the console to try things out. To open the console in Chrome and Firefox on Windows machines it is typically CTRL-SHIFT-I (the letter I).

Box 2: Another place to put code

When you make a button (or other element), you can attach code to it right in place, as in this example (again, look at the console and the code). Since this is the second box,

In general, we prefer not to put much code hidden inside of the html. It’s fine for short things, but when the code gets complicated (as it will), it’s easier to have it in a separate file.

Just to make sure you’re reading along, change this box so that when you press the button, the message in the console is a grammatically complete sentence like “Button one was pressed.” (yes, there are points on the rubric for this).

  • change text on button press (Box: 01-03-02) (Points:2)

Box 3: Where to put code

If we’re not supposed to put JavaScript into the HTML files, the next question is where to put it.

We will put JavaScript programs into separate files - dividing them up into smaller chunks (roughly “modules”), but that will come later. By convention, I will always name my JavaScript files with ‘.js’, although other people will do other things (for example, we might name the files with ‘.es6’ to signify that we’re using the ES6 version of JavaScript).

Here’s another simple example:

For something this simple, having things split between 01-03-03.html and 01-03-03.js may not be a big deal, but its a good habit to get into.

If you’re curious (and you should be) the type="module" in the <script> tag tells the browser that we want the “modern” version of JavaScript, and that we want to treat the file as a separate module. You can see the explanation in the HTML documentation for the script tag, but, basically for class we’ll always use it.

Box 4: When to run code

There is a question of when those little snippets of code get run. In some cases, it’s simple: for the button in Box 2, we assigned code to the onclick event of the button, so the code was run when this even happened (we’ll discuss events more later). But, for the code in the script tags (especially when the script tag loads another file), the code will be executed when the script tag is processed - and there’s no guarantee of what order that will happen in. This becomes even more complicated because the web browser can do things asynchronously - it doesn’t wait until the first thing finishes before starting the second. This creates a potential problem: we want our JavaScript program to access our web page, but we don’t know if the web page has finished loaded (and is ready to be accessed) when our program is run.

Note: We are showing you the “old school way” to defer execution until after the page loads. We’ll show you the newer method later. We’re purposefully showing you the old way since it is instructive. And it will be useful later.

This is an example of something that happens a lot in Javascript: old code, that was written to work with an old version of the language or browser, had to have workarounds. You need to be able to read this old code, even if you know that the modern way of doing things is easier.

The answer to this will be to delay running our JavaScript program until after we’re sure that the web page is loaded. To do this, we’ll put our program (the place to go when we want to “start”) as a function, and set this function to be called after the window finishes loading. The following is a trivial example. It’s inside the script tag (remember you are supposed to read the code for this, and all files - and you should know this one is 01-03-04.html).

When you look at the code, notice (look in 01-03-04.html ) how there is a script inside the paragraph (like in box 1), but there is also a script at the beginning. This script creates a function (that is not named) and assigned to window.onload (the handler for the event that occurs when the window finishes loading). Because this function is called after the window is called when the window is finished loading, it happens after processing the web page (so it’s output happens after the other output).

Make sure you understand this simple example - as it is quite important. It uses some key concepts (like creating functions and assigning them to handlers).

In the example, I chose to define the function anonymously. I could have used the other function declaration syntax:

window.onload = function () { 
    console.log("Box 4's output")
function main() {
    console.log("Box 4's output");
window.onload = main;

These are pretty much equivalent. The version on the right names the function main so it can be used elsewhere, but it means we need to be careful not to use it again by mistake. By not naming it, we prevent it from being used again.

There is one other subtlety here: when we embed 01-03-04.html as an iframe on this web page, it is treated as a separate web page. The window referred to in the code is the inner web page (not the outer one). The onload happens after the inner web page is finished loading. On a web page like this (with many embedded web pages), we may not know what order the different web pages will finish loading. This generally isn’t an issue, because usually we don’t do much embedding.

Almost always, when we add code to a web page, we will set things up to run after the page finishes loading.

Box 5: Multiple Start Functions

Having a start function is so useful that we often want to have many of them. But, the window has only a single onload function. So, if we tried:

window.onload = function() { console.log("Statement 1"); };
window.onload = function() { console.log("Statement 2"); };
only statement 2 would get printed (the first anonymous function would never get called). When we set the handler, we are being impolite - we simply over-wrote whatever was there first!

In this case, it’s easy to fix things (because the 2 statements are in the code next to each other, we could re-write it as one function). More generally, the solution is to have the function call the function that was previously defined.

A simple workaround is to have each function remember what function was defined before it. We could replace line 2 above with:

let oldOnLoad = window.onload;
window.onload = function() { 
    if (oldOnLoad) oldOnLoad();
    console.log("Statement 2"); };
Notice how we store the old handler in a variable `oldOnLoad`, and then call the handler on line 3. The if statement checks to make sure there really was an old handler - if there wasn't one, we can't call it.

There are many downsides to doing things this way - you have to change each of your functions, and you need to invent a variable for each one to store the information. A more elegant implementation is given in 01-03-05.html. We define a function called addStart that adds things to a list of functions that should be called at window onload.

We’ve given you addStart, so you don’t need to write it yourself (it will be part of future workbooks). However, you should look at the code for it carefully and make sure you understand it. It’s a nice example of the use of a closure - an important tool in functional programming that we will use a lot in class. We’ll talk more about functional programming in class and later in this (and future) workbooks.

Of course, in the future, we’ll put our Javascript in separate files.

The modern way to defer loading

The “modern” way to have a script run after the page has finished loading is to specify that in the script tag.

Normally, a script tag that loads a file, such as <script src="01-03-03.js"></script>, gives the browser some flexibility in when it chooses to run the script. However, we can add the defer flag like this: <script src="01-03-03.js" defer></script> which tells the browser to run the script only after the page has finished loading. This is explained on the W3 Schools documentation on defer and HTML documentation for the script tag.

Unfortunately, this only works when we load scripts from a file (which is the usual thing to do, but wouldn’t work in the examples above). Most of the workbook code still uses the windows.onload method - mainly for historical reasons.

Functional Programming, Javascript, and CS559

You may have noticed that we introduced functional programming concepts (like closures) pretty early on, and we are making a big deal about them. Part of this is that they are a nice feature of Javascript. Historically, they were the flexible building block that were used before Javascript had other features.

But, the big reason for emphasizing them early in the class is that they really are a convenient way to do graphics programming. They will come up a lot in the programming we do for class. So, it’s good to get familiar with them now.

Part of the reason for this is that we like to (or, as we will see on the next page, are often forced to) think about out programs in terms of functions that get called when events happen. In the examples so far, we had functions that executed when a button was pressed or when the page finished loading - each of these are events.

It’s worth looking at a simple example before we move on… Here is Box 01-03-02, re-written to put the script in a different place.

This is now example 01-03-06.html. Let’s look at the key lines…

let button = document.getElementById("button1");
button1.onclick = function() {
    console.log("Button 1 Pressed!");

Line 15 finds the button by searching for an HTML element on the page with the name button1. Good programming practice would have checked that button was actually found. We’ll do this often - we find objects on the web page by ID and do things to them.

The more interesting thing is what we do to the button: we assign something to its onclick property. This stores a function that gets called when the button is pressed. But notice that we put the definition right in the assignment. We are defining the function in place. You can think of the function keyword not just as defining a function, but as effectively running the compiler and producing a “function object” that can be stored in an attribute of the button (or any other variable).

Effectively, the code inside the braces on lines 16-18 doesn’t get compiled until the function expression is executed. Line 17 doesn’t get executed when this code is run - it just gets fed to the compiler. The output of the compiler - which is a “function object” - can be produced and stored somewhere, and called later.

(note: this idea of “running the compiler” is a simplification of what really happens in order to give you a mental model of how things work)

The fact that we’re effectively running the compiler as our code runs brings up interesting possibilities. We can define new functions as our program is running, and those functions have access to the current state of the program.

Here’s a slightly different example…

You can see this in 01-03-06.html. The code is a little different…

let number = 1;
let button = document.getElementById("button1");
button.onclick = function() {
    console.log(`Button ${number} Pressed!`);
number = 2;

The first difference you may notice is line 16 - rather than printing a normal string, I am printing a string with a variable in it. The back ticks “`” tell Javascript that this is a template literal - a cool javascript feature that lets me access data within a string. You can read the Template Literals Documentation to find out about them (recommended, they are quite useful). However, for now, trust me that what this does is print the value of the variable number in the middle of the string.

So, the big question… what is the value of this variable?

A thing to notice is that the variable number is declared outside of the function we are creating. When we “compile” the function, the variables that enclose the function (defined outside, but available inside) are kept. This is called closure - it is a fundamental functional programming concept. It is tricky to learn. Most students have to hear it several times before they get it. I will explain it multiple times during class. There will (almost certainly) be exam questions involving closure.

Consider what happens with this program. When the code is executed, function calls the compiler. At this point, the variable number is 1. However, line 16 doesn’t execute - it is just compiled. It keeps a reference to the variable number. This is closure. After the compiler is done creating the function number is set to 2. Later on, when the button is pressed, the function is executed. At this time, it refers to the number, which has value 2.

That’s a closure. It lets functions access data defined outside. We’ll see a lot more examples. Don’t worry if you don’t get it the first time.

Moving On…

So now we’ve seen where code can go and how functional programming lets us define the functions we can put in various places. Now let’s see how to make it do something useful on the Next Page

Next: Doing things to HTML

Page Rubric
  • (Box: 01-03-02) (2 pts) change text on button press