You can learn about about lions, tigers, or bears.
Or you could just relax and watch a video.
View the slides at github.com/gdiseattle/gdi-intermediate-js.
function add(a, b) {
return a + b;
};
add(5, 4);
Review
ES6 Functions: http://bit.ly/2phTmIi
// Named function declaration
function myFunction () { /* logic here */ }
// Assignment of a function expression to a variable
var myFunction = function () { /* logic here */ };
// Assignment of a function expression to a property
var myObj = {
myFunction: function () { /* logic here */ }
};
function
var nameImprover = function huzzah(name, adj) {
return 'Admiral ' + name + ' Mc' + adj + 'pants';
};
The function name is scoped to the defined function.
Let's say you have a function defined with a function declaration, and you try to call it in your code before it's defined in the code:
saySomething('hello');
function saySomething(text) {
console.log(text);
}
ES6: https://codepen.io/niccipeeps/pen/ZxWpeQ
It works! The function gets hoisted, or moved to the top of its scope.
This is both good and bad. Good because it keeps our code from throwing errors. Bad because it allows us to develop bad habits in our code.
Function expressions do not get hoisted. So this won't work:
saySomething('hello');
var saySomething = function(text) {
console.log(text);
}
You must define the function expression before it's called.
var saySomething = function(text) {
console.log(text);
}
saySomething('hello');
ES5: http://bit.ly/intermediate-js-function-expressionsGenerally, best practices favor using expressions over declarations for defining functions.
if
statement.We can also use a function expression to create a function that is invoked immediately - an IIFE.
// Anything within the parentheses is part of an expression
(function () { /* logic here */ }());
or
(function () { /* logic here */ })();
It starts with a (
not with function
.
The primary reason to use an IIFE is to obtain data privacy. But other reasons include:
Wikipedia has some great examples - https://en.wikipedia.org/wiki/Immediately-invoked_function_expression
We can use the parentheses to pass arguments to an IIFE:
(function sayHello(name) {
alert("hello, " + name + "!");
})('Natalie');
Inside a function, we can use the arguments
keyword to fetch an array of all the arguments passed to a function.
var addTwo = function(a, b) {
console.log(arguments); // logs [3,10]
return a + b;
};
console.log(addTwo(3, 10, 11, 14, 16));
arguments
var addMany = function() {
console.log(arguments);
var sum = 0;
for (var i=0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
};
console.log(addMany(3, 10, 57, 24));
console.log(addMany(3, 10, 57, 24, 200, 300));
var nameLogger = function(name, adj) {
if(adj === undefined) adj = "Fancy";
var newName = 'Admiral ' + name + ' Mc' + adj + 'pants';
console.log(newName);
};
this
Each time we create a function, JavaScript creates a keyword called this
.
We use this
similarly to a variable, but we can't change the value.
The keyword this
refers to the object inside which the function is created.
Huh?
Let's look at some examples.
this
inside methods of objectsvar natalie = {
living: true,
age: 42,
getAge: function(){
return this.age;
}
};
console.log(natalie.getAge());
Here our function is a method inside an object - so this
refers to the object, natalie
this
inside callback functions
var btn = document.getElementById('myBtn');
btn.onclick = function(){
var text = this.innerHTML;
console.log(text);
};
A callback function is connected to a DOM element through an event handler - so this
refers to the DOM element that fired the event - in this case, the button
that was clicked.
function trySomething(){
console.log(this);
}
trySomething();
window
objectA window
object is created in the browser as soon as the JavaScript interpreter starts.
All globally-scoped functions and variables are actually properties and methods of the window()
object.
window
objectvar num = 7;
var name = "Natalie";
console.log(window.num);
console.log(window.name);
function sayHello() {
console.log("Hello there!");
}
window.sayHello();
http://bit.ly/intermediate-js-window-object
Don't forget! Functions in JavaScript are objects.
They have properties and methods just like any other object.
Let's take a look at a few methods.
bind()
Use the bind()
method to explicity set the value of this
without invoking the function immediately.
this.name = 'Ollie'
var cat = {
name: 'Artie',
callCat: function() {
console.log('Come here, ' + this.name);
}
};
cat.callCat(); //Come here, Artie
var catCall = cat.callCat;
catCall();
// Come here, Ollie - The function gets invoked at the global scope
// global var name with module's property name
var catCall = cat.callCat.bind(cat);
catCall(); //Come here, Artie
http://bit.ly/2GtTxIG
call()
Invokes the function, allows you to explicity set the value of this
and pass in arguments one by one
function Animal(name, age) {
this.name = name;
this.age = age;
}
function Cat(name, age) {
Animal.call(this, name, age);
this.category = 'cat';
}
function Dog(name, age) {
Animal.call(this, name, age);
this.category = 'dog';
}
var cat = new Cat('Artie', 5);
var dog = new Dog('Roscoe', 40);
http://bit.ly/intermediate-js-call
Check them out here
apply()
isGenerator()
toSource()
toString()
Functions can be nested inside one another:
function showRelationship(personName, catName) {
var intro = "Please meet ";
function makeSentence() {
return intro + catName
+ ' and his human, ' + personName;
}
console.log(makeSentence());
}
showRelationship('Natalie', 'Ollie');
An inner function has access to three scopes: its own scope, the enclosing function scope, and the global scope.
http://bit.ly/intermediate-js-inner-functionsQuestions about functions before we move on?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test Page</title>
<style>
h1 {
color: red;
}
</style>
</head>
<body>
<h1>My Page</h1>
<p>Hello World!</p>
<img src="http://placekitten.com/200/300" alt="cat"/>
</body>
</html>
document
document = {
head: {
children: [ ... ] // contains all child nodes
},
body: {
children: [ ... ] // contains all child nodes
hasChildNodes: function() {
//returns true or false
}
},
getElementById: function(arg) {
//returns matching node list
}
...
}
Note: This is not complete! But gives an idea...
In the brower console, try these...
document.body
document.body.children
document.querySelectorAll('p');
What other properties and methods do we know?
By CSS selector:
document.querySelectorAll('section h2');
By tag name:
document.getElementsByTagName('div');
By class name:
document.getElementsByClassName('upper');
By ID:
document.getElementById('kittenPic');
By CSS selector:
document.querySelectorAll('section h2')[0];
document.querySelector('section h2');
By tag name:
document.getElementsByTagName('div')[2];
By class name:
document.getElementsByClassName('upper')[1];
Any questions on selecting or creating DOM nodes?
So far, we have only used specific event handler properties to associate a function with an event.
These properties have a limitation: you can only assign one callback per event.
var btn = document.getElementById('testButton');
btn.onclick = function() { console.log('did stuff #1'); };
btn.onclick = function() { console.log('did stuff #2'); };
If you click the button, you'll see that only did stuff #2
is logged to the console. We're assigning properties to an object, and the second one simply overwrites the first.
addEventListener()
methodIf we want to get fancy (or just build more stable and flexible code), we can use the addEventListener()
method instead.
element.addEventListener(event, callback);
addEventListener()
with an anonymous functionvar myButton = document.getElementById('testButton');
myButton.addEventListener('click', function() {
console.log('button was clicked!');
});
This works, but we can't remove this callback later on because we don't have a way to call it - it's anonymous and has no name.
addEventListener()
with a named functionvar myButton = document.getElementById('testButton');
//store and define the function
var myCallback = function() {
console.log('button clicked!');
};
myButton.addEventListener('click', myCallback);
ES5: http://bit.ly/intermediate-js-addEventListener
We can remove that event listener later on:
myButton.removeEventListener('click', myCallback);
Arguments get passed to the callback:
GDI
var myLink = document.getElementById('testLink');
//store and define the function
var myCallback = function(e) {
e.preventDefault();
console.log('Not going there!');
};
//no argument needed for binding the event
myLink.addEventListener('click', myCallback);
http://bit.ly/intermediate-js-arguments-and-callbacks
Add this button to your HTML page:
<button id="messageButton">Show a message</button>
Now add an event listener to the button that will log a message to the console when the button is clicked.
Bonus: If you finish early, try updating the callback so it logs the number of times the button has been clicked to the console.
The DOM is a series of objects, nested inside one another: a paragraph inside a div inside the body inside the document, for example.
When an event is triggered on a nested element, it's also triggered on the parent(s), all the way up to the document
object.
Really?
This is a paragraph
var div = document.getElementById('parent');
var showMessage = function(e) {
console.log('The div was clicked!');
}
div.addEventListener('click', showMessage);
http://bit.ly/intermediate-js-bubbling
The order is called a bubbling order, because an event bubbles from the innermost element up through parents, like a bubble of air in the water.
The deepest element that triggered the event is called the target
. And this
is the element the event has bubbled to - the one that triggers the event handler.
The target
is stored as a property of the event object.
var div = document.getElementById('parent');
var showMessage = function(e) {
console.log('You clicked on ' + e.target.nodeName);
console.log('The event was handled on ' + this.nodeName);
}
div.addEventListener('click', showMessage);
stopPropagation()
You can stop an event from bubbling up the DOM with the stopPropagation
method.
var div = document.getElementById('parent');
var btn = document.querySelector('#parent button');
var showMessage = function(e) {
console.log('You clicked on ' + e.target.nodeName);
console.log('The event was handled on ' + this.nodeName);
}
var showAnotherMessage = function(e) {
e.stopPropagation();
console.log('The ' + e.target.nodeName + ' was clicked and the event didn\'t bubble.');
};
div.addEventListener('click', showMessage);
btn.addEventListener('click', showAnotherMessage);
We can take advantage of event bubbling in our code.
Let's start with this HTML:
- Maru - he likes boxes!
- Hana - she love Maru
- Lil Bub - she's a trooper
- Grumpy Cat - she's the grumpiest
We want to toggle a highlight on each list item when it's clicked.
We could add an event listener to each list item...or we could just listen for an event on the list:
var list = document.getElementById('awesomeCats');
var listClicking = function(e) {
var el = e.target;
while (el != this) {
if (el && el.nodeName == 'LI') {
toggleHighlight(el);
}
el = el.parentNode;
}
};
var toggleHighlight = function(el) {
var color = el.style.backgroundColor;
if (color) { el.style.backgroundColor = ''; }
else { el.style.backgroundColor = 'pink'; }
};
list.addEventListener('click', listClicking);
http://bit.ly/intermediate-js-event-delegation
The addEventListener() method only fires when the page is first loaded.
If we change the page later - e.g., we dynamically add more items to the list - those new items won't have event listeners. We would have to re-add event listeners to any new items created on the page.
Or we can use event delegation instead - the container element stays on the page no matter what, and we can listen for events there.
Not all events bubble - here's a nice list of events that shows which ones bubble and which do not.
If you're not careful, you can put a lot of load on the browser - be sure to think about how often an event might be fired vs how often you'd want to trigger a callback function in response.
Given this HTML:
You can learn about about lions, tigers, or bears.
Or you could just relax and watch a video.
Use event delegation to display an alert each time a user clicks a link that asks them if they're sure they want to navigate from this page.
Questions on event bubbling and delegation?
Two things for you to do before next week's class: