Javascript Research Presentation: In class, you will be assigned an aspect of browser-based Javascript. Prepare a 5 minute presentation that describes and demonstrates this topic. Create an interactive graphic to use in your presentation that implements the functionality, and be prepared to show your code. Everybody will present to the class on week 7.
A good place to start might be searching for yourTopic + " JavaScript MDN";
(you'll have to evaluate that code in your head, I'm afraid!)
In class, we covered some of the ways we can use JavaScript to interact with the document of our webpages. JavaScript lets us access, modify and create HTML elements and styles on our page. It also lets us listen for events that allow users to interact with our page. This is called "DOM manipulation." The DOM, or Document Object Model, is what's happening inside the browser window. It's not an individual HTML, CSS or JavaScript file, but rather how they combine to create what we actually see on the screen. To demonstrate DOM manipulation in class, we made a "dice roll" program that generated a random number between 1 and 6 when a user clicks a button.
Let's recap! First, let's make an HTML page with two things: one, a script tag that links to a file where we'll put all of our JavaScript, and two, a <button>
tag with an ID and some content.
<!DOCTYPE html>
<html>
<head>
<script src="week6.js";></script>
</head>
<body>
<button id="myButton">Click Me!</button>
</body>
</html>
If you load this in your local host, you'll see a button that does nothing. This is all the HTML we need - everything else will take place in the JavaScript!
Before we can do anything with the DOM, we need to wait for it to finish loading. Our JavaScript file is just one part of the DOM and the web browser might finish downloading it before it's finished loading the HTML. We're not going to be able to access any HTML elements if our JavaScript runs before it's been loaded. Fortunately, the DOM will let us know when it has finished loading by triggering an event.
Events are messages that the web browser sends us when certain things happen. We will want to write code that only happens when a certain action has taken place - the DOM has loaded, the user clicks a button, the user scrolls down on the page, etc - but we don't want that code to run immediately. Waiting for an event to trigger is called listening, and reacting to the event is called handling. To demonstrate this, let's write some code in our browser.
function onWindowLoaded(event) {
console.log("loaded");
}
window.addEventListener("load", onWindowLoaded);
You should recognize some things from last week: calling a function, passing parameters (one of which is a string). But everything else is new, so let's go through them.
The first new thing is the very first word: function
. Like var
, if
or for
, function
is a special word in JavaScript. What we're doing here is creating a function. Before, we've only used functions that already exist, but here we're making our own.
Functions come in four parts: first, the word function
. Second, the name of our function, which in this case is onWindowLoaded
. We can name our functions whatever we want, but I chose this name because it describes the role of the function: it contains code that is going to be run when the window has loaded. Third are two parentheses with the word event
in between. This defines the parameters of our function, which in this case is one variable named event
. Finally are the two curly braces. Everything inside of these curly braces is the code of our function. When we call this function, everything inside the curly braces will be run, but not before then. In this case, we're just going to log out a string to the console.
Whereas before we've called functions manually (think "Hello".toUpperCase();
), now we're going to let the browser call the function for us. This happens on the next line, window.addEventListener("load", onWindowLoaded);
.
window
is a special variable given to us by the browser. It references the browser window and contains some of the same functionality you would use the browser window for in your day-to-day web browsing. For example, try logging window.location.href
or calling window.location.back()
from your console. It also just so happens to be the thing that tells us when everything has loaded.
The window
contains a function that we are going to see in a lot of different places: addEventListener
. This function takes two parameters: first, the name of the event, and second, the function to call when the event is triggered. In this case, the name of the event is "load"
and the function is the one we defined at the top of our code, onWindowLoaded
. When we're passing a function to addEventListener
, we only need to pass the name of the function. We can leave out the two parentheses we would usually use to call the function because we don't want to call it right away - the browser will call the function for us. How kind!
If you reload your page now, you should see "loaded"
logged to the console. This isn't particularly exciting because the "load"
event happens almost immediately. But without it, we can't do the next part: accessing HTML elements from the DOM.
The next thing we need to do to get our dice program to work is wait for someone to click the button. We already know how to wait to do things - with events - but how do we add that event listener to the button? Enter getElementById
.
When we were working with CSS, we used the ID attribute in HTML to apply styles specifically to one element. We can use the same ID in JavaScript to bring the entire element in to our code. Let's add some code in to onWindowLoaded
to do this for us.
function onWindowLoaded(event) {
var myButton = document.getElementById("myButton");
console.log(myButton);
}
window.addEventListener("load", onWindowLoaded);
The beginning of the new line, var myButton =
, should sound familiar. We're declaring a variable called myButton
and giving it a value. On the right side of the equals sign are a few new things. First, document
, which is a special variable much like window
. The document
is what you see when you open up your web inspector to the first tab, Elements. It's everything from the opening <html>
to the closing </html>
. It also contains a whole bunch of special functions that let us manipulate the DOM: add and remove HTML, style elements, change words, etc, all from within JavaScript.
One of these special functions is getElementById
. It takes one parameter: a string that represents the ID of the element you want to get. There are some more functions to do similar things that we'll see next week like getElementsByClassName
and getElementsByTagName
. In your console, you should see the element logged out, and if you hover over it, your browser should highlight the button on our page.
Now that we have a reference to the button, we can add an event listener for the "click"
event so we know when someone has clicked the button.
function onWindowLoaded(event) {
var myButton = document.getElementById("myButton");
myButton.addEventListener("click", onButtonClick);
}
function onButtonClick(event) {
console.log(event);
}
window.addEventListener("load", onWindowLoaded);
There's nothing new here besides the name of the click event, which, conveniently, is called "click"
. By the way, there are many events that we can listen for. Here's a big list!
Now when we click our button, we'll see the event
variable logged out in our console. We're not going to get in to this a whole lot right now, but it's worth seeing why it's there. It contains a lot of information about the event itself, like when it happened, what caused the event to trigger, and in the case of click events, where on the page it happened (see the pageX
and pageY
properties).
Now that we know when our button has been clicked, we can actually build our dice functionality! We have two options for creating a place for our dice roll output to live. We could add a tag in the HTML, style it with CSS in a separate CSS file, and use getElementById
to get the element so we can change its contents. But we can also do all of this with of JavaScript.
We're going to write two new functions to help us with this: one to create an element where our result will be displayed, and one to generate the random number for the dice.
var diceRollElement;
function onWindowLoaded(event) {
var myButton = document.getElementById("myButton");
myButton.addEventListener("click", onButtonClick);
}
function onButtonClick(event) {
}
function createDiceElement() {
}
function updateDiceValue() {
}
window.addEventListener("load", onWindowLoaded);
We also create a new variable at the top called diceRollElement
. We're going to refer to this variable in multiple functions, so it needs to be declared outside of the curly braces of those functions. Because we aren't using the =
sign here, the variable is undefined.
The function we use to create elements is document.createElement()
. It takes one parameter, which is the name of the element. It will be the same as all of the HTML tags we've written before, so "p"
, "h1"
, "button"
or anything else are all valid parameters depending on what type of element you want to create. Let's do that in our createDiceElement
function.
function createDiceElement() {
diceRollElement = document.createElement("h1");
}
It's worth noting that functinos like createElement
or getElementById
all produce the same type of variable. They're called HTMLElement
s. It's not built in to JavaScript like strings or numbers, but rather it's built in to the web browser (a triviality as far as we're concerned). All you need to know is that everything that we're going to do to this new element, we could do to a pre-existing element as well.
The same way that we can apply styles to an HTML element in CSS, we can do in JavaScript using the style
property of our new variable. Let's add a few more lines to our createDiceElement
function:
function createDiceElement() {
diceRollElement = document.createElement("h1");
diceRollElement.style.color = "red";
diceRollElement.style.fontSize = "50px";
}
This should look familiar - first, the CSS property, and then its value. The syntax is a little different (note how dashes in CSS are replaced with camel case letters, e.g. font-size
in CSS is fontSize
in JS), but the concept is the same.
The last thing we need to do here is add the element to the page. We're going to use the document
for this, specifically a function on document.body
called appendChild
.
function createDiceElement() {
diceRollElement = document.createElement("h1");
diceRollElement.style.color = "red";
diceRollElement.style.fontSize = "50px";
document.body.appendChild(diceRollElement);
}
With this, our diceRollElement
, along with all the properties we set on it, get appended (meaning "placed at the end of") to document.body
, which is the part of the DOM that we can see - everything between <body>
and </body>
.
We're not calling createDiceElement
yet, but that's fine for now. Let's shift our attention to generating a random number to place in it, so we have something to see. Last week we learned how to generate and scale a random number from 0 to the length of an array. A dice roll is pretty much the same thing, except we want to start at 1 instead of 0 and we can just hard code 6 as the upper limit. So hopefully this all looks familiar:
function updateDiceValue() {
var diceRollValue = Math.random() * 6;
diceRollValue = Math.floor(diceRollValue);
diceRollValue++;
}
We generate a random number between 0 and 1 (specifically, Math.random()
generates a number from and including 0 to just under 1, or .999 followed by lots of 9s), scale it up to be from 0 to 6, floor it down to remove decimals, and then add 1.
The last part of updating the value of the dice roll is to add our random number in to our diceRollElement
. For this, we use the innerHTML
property of our diceRollElement
.
function updateDiceValue() {
var diceRollValue = Math.random() * 6;
diceRollValue = Math.floor(diceRollValue);
diceRollValue++;
diceRollElement.innerHTML = diceRollValue;
}
innerHTML
is a property that is equivalent to what goes between the tags in a normal HTML element. Since diceRollElement
represents an <h1>
tag, this is as if we typed <h1>(Some Random Number)</h1>
.
Finally, we need to call these two functions to complete our program. Let's return to our onButtonClick
function. The logic we want here is to say "if we don't have a place to put our random number, create one. Regardless, generate a random number and put it on the screen."
function onButtonClick(event) {
if (!diceRollElement) {
createDiceElement();
}
updateDiceValue();
}
One last new thing here, which is the !
in the if statement. !
means "not." !diceRollElement
is true because the first time we click the button, diceRollElement
hasn't been defined yet. We use this opportunity to create the element by calling out createDiceElement()
function, and then every subsequent time we click the button, diceRollElement
exists and that code will be skipped over. Finally, we call updateDiceValue()
.
Here's the finished JavaScript:
var diceRollElement;
function onWindowLoad(event) {
var myButton = document.getElementById("myButton");
myButton.addEventListener("click", onButtonClick);
}
function onButtonClick(event) {
if (!diceRollElement) {
createDiceElement();
}
updateDiceValue();
}
function createDiceElement() {
diceRollElement = document.createElement("h1");
diceRollElement.style.color = "red";
diceRollElement.style.fontSize = "50px";
document.body.appendChild(diceRollElement);
}
function updateDiceValue() {
var diceRollValue = Math.random() * 6;
diceRollValue = Math.floor(diceRollValue);
diceRollValue++;
diceRollElement.innerHTML = diceRollValue;
}
window.addEventListener("load", onWindowLoad);
And here it is in action! (I have changed the code running on this page slightly because I don't want the number to appear at the bottom of the page. Think about how I could have done that - hint: getElementById
- and then view the source code for this page to see if you got it right!)