Skip to content

Latest commit

 

History

History
369 lines (249 loc) · 10.5 KB

README.md

File metadata and controls

369 lines (249 loc) · 10.5 KB

DOM Events

Why is this important?

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.

What are the objectives?

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.

Where should we be now?

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.

Aside: Callbacks

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

Events are always happening!

Click Event

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.

Event Listeners

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.

  1. Can you capture the scroll event?
HintThe subject listening should be the `window` object - try using `$(window)`).
Add a simple event handler to `console.log` a message every time the event occurs.
answer ``` javascript $(window).on("scroll", function handleScroll(){ console.log("just keep scrolling, scrolling, scrolling"); }) ```
  1. 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 ``` javascript $(window).on("scroll", function handleScroll(){ $("body").append("

to infinity... and beyond!

"); }) ```

Default Behaviors

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;
});

Event Targets, Bubbling, Delegation

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.

Event Targets

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);
});

Event Bubbling

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");
}

Independent Practice

Practice with this event medley training.

Closing Thoughts

  • 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!