JavaScript I – Day 8
Geekwise Academy
Instructors:
Corey Shuman
Aaron Lurz
Slack Channel:
https://geekwise-js1.slack.com
Github:
https://github.com/coreyshuman/Geekwise-JavaScript-I
Lesson plans:
http://coreyshuman.github.io/Geekwise-JavaScript-I/html/
· Review Day 7 Assignment
· Programming Challenge
· More on Functions
o Function Scope
o Function Expressions
o Anonymous Functions
o Hoisting
· DOM Manipulation
o Selecting and accessing elements on the page
o Element Attributes
· Create a new function called dinosaur that has a signature dinosaur(type, era, food)
· Your function should return an array containing the type, era and eating habits (carnivore or herbivore) passed into the function.
· Create a second function that contains a for loop.
· Create an empty array for dinosaurs.
· Prompt the user for how many dinosaurs they want to input.
· The for loop should run the number of times specified by the user.
· In the loop prompt the user for the type of dinosaur, the era in which it lived and it's eating habits.
· Call the dinosaur() function in each loop with the user supplied info and push the returned arrays onto your dinosaur array
· After the user has input all of their dinosaurs, create a second loop to loop over the dinosaur array of arrays and create a console.log or an alert stating the type era and eating habits of each dinosaur. Instead of console.log or alert, you could also try printing the information to the page using some of the DOM information we learned on Monday (or peak ahead in the notes for today).
Every JavaScript program runs with a "scope". This can be thought of as the things that are currently available to the execution of a program. The main / base scope is referred to as the "global" scope.
Creating a function in JavaScript creates a new scope.
One important thing to understand is that code outside of a function cannot access any code inside the function.
function foo() {
var a = "inside foo";
return a;
}
console.log(a); //undefined, because "a" is "scoped" to the foo function
console.log(foo()); //should log "inside foo" because the function was called, and a value returned
So now we know that code outside of a function can't access anything other than what's returned from a function. However, the code inside a function can access anything that is outside of the function.
var name = "adam";
function capitalize() {
return name[0].toUpperCase() + name.slice(1);
}
var capitalizedName = capitalize();
console.log(capitalizedName);
Functions do have access to the variables and other functions that exists in outer scopes (which, when you think about it, is why you can console.log(...) something inside of a function.
So now we know how to write a function, but there are other ways to do it as well. You'll surely encounter them in the wild so it's nice to know what's going on when you see them.
Functions in JavaScript are "first class". This means they can be passed as arguments to other functions, returned from other functions, and assigned to variables (instead of being a named function).
When a function is assigned to a variable, it's called a function expression.
Try it: Create a function expression that assigned a function to a variable.
var capitalize = function (str) {
return str[0].toUpperCase() + str.slice(1);
};
So what is different here? Notice, the function declaration does not have a name. This is what is known as an Anonymous function, or a function with no name.
If it has no name, how do we call it? Same way we do with other functions, but we use the variable name instead. In the case of the capitalize function above, we can call it as capitalize("adam").
Try it: Create and execute a function expression.
var capitalize = function (str) {
if (!str) {
return "";
}
return str[0].toUpperCase() + str.slice(1);
};
console.log(capitalize("adam"));
One thing to note about anonymous functions, they don't actually have to be anonymous. In fact, it's good practice to name all your functions, for the purposes of error reporting. The function below will work just the same.
var capitalize = function capitalize(str) {
if (!str) {
return "";
}
return str[0].toUpperCase() + str.slice(1);
};
console.log(capitalize("adam"));
Now, let’s try an experiment. Create a new function (not a function expression) called logName. Then, above that code, call logName. What would you expect to happen?
logName("adam");
function logName(name) {
console.log(name);
}
This is due to something JavaScript is doing called hoisting.
Even though JavaScript executes linearly, variable and function definitions are actually "hoisted" to the top of the scope. So even though we call logName before we define it, JavaScript is actually recognizing the definition of the function, hoisting it to the top of the scope, which makes it available to call before the definition in the code actually happens.
Is the same true of function expressions?
Try it: Call a function expression before it is defined. What happens?
foo("adam");
var foo = function (name) {
console.log(name);
};
So what happened there? Why did we get the undefined is not a function?
The reason for this is that only named function definitions and variable declarations are hoisted. The values of the variables are not hoisted.
What would you expect to happen in the example below?
var name = "Adam";
function alertName() {
alert(name);
var name = "Becky";
alert(name);
}
alertName();
What is the DOM?
The Document Object Model (DOM) is a programming interface for HTML. It allows JavaScript to manipulate an HTML page after it's been loaded in the browser.
The DOM provides a representation of the document as a structured group of nodes and objects that have properties and methods.
JavaScript wouldn't be of much use to us if we couldn't use it to manipulate HTML pages in some way.
In order to begin manipulating elements on the page, we need to know how to "reference" those items. For example, if we want to change our pages <h1> tag, we need to be able to tell JavaScript which node on the page we want to change.
So, using the example above, lets setup an HTML page with an h1 tag in it.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1 id="myHdr">This is an h1 tag</h1>
<script type="text/javascript" src="./static/js/day7.js"></script>
</body>
</html>
The DOM gives us some nice ways to select elements on a page for manipulating. The first method we'll discuss is called document.getElementById().
Let’s select the h1 tag using the getElementById function. It takes as an argument the id of the element you want to select, myHdr in this case.
var h1 = document.getElementById("myHdr");
console.log(h1);
Run that page in your browser, you should see the element logged to your console.
Now type h1. (notice the period). The console should now show you all the various properties and functions available as part of that object.
You can now see that a "node" or "element" has an underlying JavaScript object that provides all kinds of methods for you to interact with it. Let’s look at some attributes. First, modify your h1 tag to include a class="hdr" attribute.
<h1 id="myHdr" class="hdr">This is an h1 tag</h1>
Now use the h1.hasAttribute() function to see if the given DOM node has a specific attribute.
var hasClass = h1.hasAttribute("class");
console.log("The h1 tag has a class attribute: ", hasClass);
Run the code above, it returns boolean so if the attribute exists, it will return true otherwise it will return false.
Cool, so we can see if an element has a specific attribute, what about adding new attributes? Easily done. Using the same DOM node we can call the h1.setAttribute(). It takes two arguments in its signature, the property name to set, and its value. Let’s add a rel attribute.
h1.setAttribute("rel", "something");
console.log(h1);
Awesome, so now we can add and look at attributes on a DOM node, I guess the only thing left is to be able to remove them. This is done with the removeAttribute function.
Let’s remove the rel attribute now.
h1.removeAttribute("rel");
These functions become really handy so practice them! You can now manipulate ANY webpage you go to that uses id's on their elements using your console!
Let’s add some markup to our html page.
<ul>
<li><a href="http://google.com">google</a></li>
<li><a href="http://yahoo.com">yahoo</a></li>
<li><a href="http://github.com">github</a></li>
</ul>
There are alternatives to getElementById for cases where an item might not have an id, or you want to use an alternative syntax. The first is document.querySelector.
var a = document.querySelector("ul li a");
console.log(a);
What does that yield? Is it what you would expect? It should return the first matched element for the specified selector.
Let’s pick the "selector" apart. We used ul li a. What this is really saying is, "Find any a tags that are children of li tags, where the li tag is a child of a ul tag. Put another way, "Find all anchor tags that are inside an li tag inside a ul".
The querySelector function always returns a node or null if no node was matched. If multiple nodes were selected, only the first will be returned.
So what if I want to select more than one node? That's where querySelectorAll comes into play.
var anchors = document.querySelectorAll("ul li a");
console.log(anchors);
console.log(anchors.length);
The querySelectorAll function returns a NodeList which looks like an array, but actually isn't. It has a length property, but doesn't have other array methods like push, pop, etc...
A NodeList object provides an item function that you can use to select a specific item at a specified index.
console.log(anchors.item(2));
console.log(anchors.item(10));
Since a NodeList looks a lot like an array, and has a length property, that means it can be used as the basis for a loop, yeah? Let’s loop over each link, and log to the console the href property of each node.
for (var i = 0; i < anchors.length; i++) {
var anchor = anchors.item(i);
console.log(anchor.href);
}
From here on out we will start focusing on the Final Project in our assignments. A detailed write-up on the project can be found on the class Github. This also means I won’t be providing weekly solutions to the assignments since it is an on-going project. Make sure you ask Corey or Patrick for help if you get stuck and don’t let yourself fall behind!
Today we will start by building the physical framework (HTML) for the ToDo App. Complete the following to keep on track: