This workshop is important because:
Most of the interactivity for JavaScript in the web is based around events. The browser detect user actions or page status changes and "emits" or sends out an event. Developers then specify behaviors that will happen when a particular event occurs.
After this workshop, developers will be able to:
- Explain what DOM events are and how they are triggered.
- Attach event listeners to DOM elements.
- Target the source of an event.
- Respond to events with a callback.
- Explain event propagation.
Before this workshop, developers should already be able to:
- Trace the control flow of a program including functions.
- Distinguish between function definitions and function calls.
- Select DOM elements with jQuery.
A callback is a function that is passed into another function as an argument and then used. A function that can take in a callback as an argument is known as a higher order function.
function higherOrderFunction (phrase, callback) {
console.log("I'm the first class function, now calling the callback...");
callback(phrase);
}
function shoutItCallback(message){
console.log(message.toUpperCase());
}
function splitItCallback(str){
console.log(str.split(""));
}
higherOrderFunction("Functions are fun!", shoutItCallback);
higherOrderFunction("functions are fun!", splitItCallback);
The callback pattern is used a lot in JavaScript. As an example, .forEach
is a built-in Array iterator method that takes in a callback.
var numbers = [123, 45, 0];
numbers.forEach(isEven);
function isEven(num){
if (num % 2 === 0){
console.log(num + " is even!");
return true;
} else {
console.log(num + " is odd!");
}
}
Often, the callback function definition is written inside the higher order function call.
var numbers = [123, 45, 0];
numbers.forEach(function isEven(num){
if (num % 2 === 0){
console.log(num + " is even!");
return true;
} else {
console.log(num + " is odd!");
}
});
In these cases, the callback often won't be given a name. A function without a name is called an anonymous function.
When you use a jQuery selector like
$('p')
, the collection you get back isn't exactly the same as a native JavaScript array. To iterate over a jQuery collection, use jQuery's.each
iterator method.
Events are always happening!
In Chrome, we can use the following utility function to log some events occurring in the window:
monitorEvents(window, ["click", "keypress", "resize", "scroll"]);
Here's a larger list of DOM event types:
click
mouseenter
,mouseleave
load
DOMContentLoaded
keydown
,keypress
,keyup
scroll
resize
change
Note: some events can only be listened to by certain DOM elements. Check documentation.
Events tell us a lot of information. For example, a "click" event includes not just the fact that a "click" occurred but also where, when and what was clicked:
* `x` and `y` screen coordinates.
* DOM element that got clicked.
* Time (timestamp) when it happened.
The browser is sending out these events all the time. In order to capture and act on them, we can add event listeners to elements. We'll use JavaScript to tell an element to listen for a certain type of event and what to do when that event occurs.
So long as we know the name of the event we're listening for, we can "attach" or "bind" an event listener to our element!
metaphor | JavaScript example | jQuery example |
---|---|---|
A subject (the thing doing the listening). | document.getElementById('greeting') | $('#greeting') |
A trigger (the "event" to listen for). | .addEventListener('mouseover', ...) | .on('mouseover', ...) |
An action (how to respond). | function popUpYay(){ ... } |
function popUpYay(){ ... } |
Here's how this looks all together with "vanilla" or "native" JavaScript:
var greeting = document.getElementById('greeting');
greeting.addEventListener('mouseover', popUpYay);
function popUpYay(event){
alert("Yay!");
}
When we've selected an element or elements with jQuery, we can use jQuery's .on
method to add an event listener:
var greeting = $('#greeting');
greeting.on('mouseover', popUpYay);
function popUpYay(event){
alert('Yay!');
}
Sometimes you will see this shorthand:
$('#greeting').mouseover(popUpYay);
The .mouseover(...)
method is equivalent to .on('mouseover', ...)
. We recommend using .on
.
####Check for Understanding
In the last example:
- What is listening for the event?
- What trigger are we waiting for?
- What is the action tied to this event?
- When is the
popUpYay
function actually executed?
####Check for Understanding
Open your developer console on jQuery.com.
- Can you capture the
scroll
event?
Hint
The subject listening should be the `window` object - try using `$(window)`).answer
``` javascript $(window).on("scroll", function handleScroll(){ console.log("just keep scrolling, scrolling, scrolling"); }) ```- Modify your event handler so it adds a new paragraph,
<p>to infinity... and beyond!</p>
, at the bottom of the page every time the user scrolls.
answer
``` javascriptto infinity... and beyond!
"); }) ```As a digital native, you take these behaviors for granted:
- When you "submit" a form, you want it to send your data somewhere.
- When you "click" on a link, you expect to navigate to a new page.
Try this on jQuery's homepage:
var $links = $("a"); // every link on the page
$links.on("click", function handleClick(event){
alert("You just clicked a link. You are about to be redirected.");
});
Redirecting to a new page is the default behavior of anchor tags (a
elements). How would we stop this behavior? What if we need to "prevent (the) default" ?
We have two options: we can return false
or we can use a special method called .preventDefault()
.
.preventDefault()
(preferred because it is explicit):
$("a").on("click", function handleClick(event){
event.preventDefault();
// more code down here
});
return false
(this works too!):
$("a").on("click", function handleClick(event){
// more code up here
return false;
});
With each event in the DOM there is a target
. For example, when a user clicks an image, the target
would be the image that was actually clicked.
Consider the following snippets:
index.html
<img id="kittenPic" src="http://petnamesplace.com/wp-content/uploads/2009/12/kitten-names-copy.jpg"></img>
app.js
var kitten = $("#kittenPic");
kitten.on("click", function (event) {
console.log(this);
console.log(event.target);
});
This might seem very straightforward, but in reality the event.target
is not always the only element that knows about the click.
index.html
<div id="kittenCon">
<img id="kittenPic" src="http://petnamesplace.com/wp-content/uploads/2009/12/kitten-names-copy.jpg"></img>
</div>
app.js
var kittenContainer = $("#kittenCon");
kittenContainer.on("click", function (event) {
console.log(this);
console.log(event.target);
});
Note that that when we click the image we also click anything containing the image - the #kittenCon
<div>
, the <body>
, the whole document
and window
objects.
###Event Delegation
Event bubbling enables a tactic called event delegation - attaching an event listener to a parent element when we actually want to listen for events on its children.
This is most useful when the children aren't there when the page loads. It's impossible to attach an event listener to something that doesn't exist yet!
Let's add a box 3 seconds after the DOM is ready to simulate content that we might have to wait for, like user input or results from an external API.
We'll try to attach an event handler to it directly, as soon as the DOM is ready.
We might try:
app.js
$(document).ready(function(){
// ...
window.setTimeout(addBox, 3000);
$(".box").on("click", toggleLongBox);
});
function addBox(){
console.log("adding a box!");
newBox = $('<div class="box"></div>');
$('#box-container').prepend(newBox);
}
function toggleLongBox(event){
$(this).toggleClass("long-box");
}
The box doesn't respond to clicks because we tried to add the event listener too early!
A common strategy is to add the event listener that will contain the elements when they exist. Usually, developers add a special container in the HMTL. We have a div set up with id "box-container"
.
The .on
method of jQuery conveniently lets us add an argument to specify which child elements should respond to an event if their parent is listening.
app.js
$(document).ready(function(){
// ...
window.setTimeout(addBox, 3000);
// $(".box").on("click", toggleLongBox); // didn't work!
$("#box-container").on("click", ".box", toggleLongCon);
});
function toggleLongCon(event){
$(event.target).toggleClass("long-box");
}
Practice with this event medley training.
-
Event-driven behaviors are the expectation for modern sites.
-
Practice selecting DOM elements. This can be done with native JavaScript language features, but we're mainly going to use jQuery library methods.
-
Remember, jQuery is a library, not a language.
-
Events bubble; we can use this to our advantage!