Lesson Tuesday

There is a DOM reference image at the beginning of the text that may be handy for visualizing DOM manipulation and traversal.

There is a DOM reference image at the beginning of the text that may be handy for visualizing DOM manipulation and traversal.

In this lesson, we are going to explore manipulating DOM elements by inserting new text with the jQuery prepend() method. Then we'll look at how to traverse the DOM in search of a specific element to remove it. Let's take a look at a visual representation of the DOM that you can use as reference for manipulation and traversal. Each element in the DOM, represented in each box below, can be a parent, child or sibling to other elements. Understanding the position of elements in the DOM will help you insert, locate and remove elements, as needed.

DOM Visual Reference


In the last lesson, we explored how to simply show and hide elements of the DOM. Now, let's get a little more advanced and insert content into the DOM. Here's some HTML we'll start with:

talk.html
<!DOCTYPE html>
<html>
  <head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-1.10.2.js"></script>
    <script src="js/scripts.js"></script>
    <title>Talk to the web page</title>
  </head>
  <body>
    <div class="container">
      <h1>Talk to the web page</h1>
      <p>Click a button to say something to the web page. See what it says back!</p>

      <button class="btn btn-primary" id="hello">Say "hello"</button>
      <button class="btn btn-inverse" id="goodbye">Say "goodbye"</button>
      <button class="btn btn-danger" id="stop">Say "stop copying me!"</button>

      <div class="row">
        <div class="col-md-6">
          <h2>You said:</h2>
          <ul class="unstyled">

          </ul>
        </div>

        <div class="col-md-6">
          <h2>The web page said back:</h2>
          <ul class="unstyled">

          </ul>
        </div>
      </div>
    </div>
  </body>
</html>

Now, here's the JavaScript to make the buttons work:

scripts.js
$(document).ready(function() {
  $("button#hello").click(function() {
    $("ul").prepend("<li>Hello!</li>");
  });

  $("button#goodbye").click(function() {
    $("ul").prepend("<li>Goodbye!</li>");
  });

  $("button#stop").click(function() {
    $("ul").prepend("<li>Stop copying me!</li>");
  });
});

The .prepend() method will insert the text of the argument it's given into to the top of <ul> as the first child of the <ul>. (As you might guess, there's also a .append() method that would insert at the bottom as the last child of the <ul>; there are also .before() and .after() methods that would add the argument before or after the <ul> tag as siblings, rather than within it as children.)

This is nice, but not a very fun conversation. Let's have the webpage say something different back to the user:

scripts.js
$(document).ready(function() {
  $("button#hello").click(function() {
    $("ul#user").prepend("<li>Hello!</li>");
    $("ul#webpage").prepend("<li>Why hello there!</li>");
  });

  $("button#goodbye").click(function() {
    $("ul#user").prepend("<li>Goodbye!</li>");
    $("ul#webpage").prepend("<li>Goodbye, dear user!</li>");
  });

  $("button#stop").click(function() {
    $("ul#user").prepend("<li>Stop copying me!</li>");
    $("ul#webpage").prepend("<li>Pardon me. I meant no offense.</li>");
  });
});

Of course, we need to change the <ul> tags to have IDs for user and webpage. For the sake of brevity, I won't bother showing the updated HTML here. Go ahead and do that yourself.

THE CODE BELOW IS TRICKY. WE JUST WANT YOU TO KNOW THE STUFF ABOVE THIS POINT, BUT WATCH THE WHOLE VIDEO.

Now that we know how to add to the DOM, let's learn how to remove from it. Let's make it so that when a user clicks a message, it disappears.

First, we need to select each message. When I'm working on some tricky DOM manipulation, I usually start by selecting the element and changing its background color to green, just so that I know I have my selector working properly. You might think this bit of jQuery would work:

scripts.js
$(document).ready(function() {
  // previous code...
  $('li').css('background-color', 'green');
});

But it doesn't do what we want. This code is run right after the web page finishes loading ($(document).ready()). And after the page finishes loading, there are no <li> elements. The elements are only added after we click a button. So we need to move our new code to within the callback passed to click(). Let's start by just adding it to one of the click() methods:

$("button#hello").click(function() {
  $("ul#user").prepend("<li>Hello!</li>");
  $("ul#webpage").prepend("<li>Why hello there!</li>");
  $('li').css('background-color', 'green');
});

Now, if we click Say "hello", the messages are green.

Let's move to the next step - attaching an event handler in place of changing the background color. Again, in the spirit of taking one step at a time, we're not going to actually try to get the click to delete the element - instead, we're going to simply open a dialog box:

$("button#hello").click(function() {
  $("ul#user").prepend("<li>Hello!</li>");
  $("ul#webpage").prepend("<li>Why hello there!</li>");
  $('li').click(function() {
    alert('hi');
  });
});

Now, if we click Say "hello" and then the message, we get our alert! But there's a problem: if we click the button twice, clicking the last message will open the alert twice. And if we click the button again, the last message will open the alert three times, and the middle message will open it twice. What's happening is that the first time we click the button, $('li') only finds a single message in each list, and attaches an event handler. But the next time we click the button, that first message is already on the page, and a second event handler is attached to it. And each time we click the button again, another event handler is attached to every message on the page.

What we want is to only attach handlers to the message we most recently inserted. Since we're inserting them at the top of each list, we can select one of the <ul>s, look through its child elements (the <li>s), and select the first one of them:

$("ul#user").children("li").first().click(function() {
  alert('hi');
});
$("ul#webpage").children("li").first().click(function() {
  alert('hi');
});

Now, each of the messages only opens a single dialog box when clicked.

Finally, we should replace our callback with the actual code to delete the message:

$("ul#user").children("li").first().click(function() {
  $(this).remove();
});
$("ul#webpage").children("li").first().click(function() {
  $(this).remove();
});

remove() is pretty straightforward, but what is this? this is a bit of a tricky concept in JavaScript, and I'm not going to give it a full explanation here. For now, you can think of it as referring to whatever was clicked on.

We're done with this rather long lesson. Congrats on making it through!

Examples


Insert within a DOM element at the beginning or end:

$('ul').prepend('<li>First list item</li>');
$('p').append('New last sentence of the paragraph.');

Insert before or after a DOM element:

$('p').before('<h3>Title for paragraph</h3>');
$('h1').after('<h2>New subheading</h2>');

Remove a DOM element:

$('#id-to-remove').remove();

Select the children of a DOM element:

$('.some-class').children(); //select all children
$('ul').children('li'); // selects just the <li>s

Select only the first or last child element for a node:

$('ul').children('li').first();
$('ul').children('li').last();

Select the element that was clicked on with this:

$('.some-class').click(function() {
  $(this);
});

Test that you've selected the correct DOM element:

$('.element-to-select').css('background-color', 'green');

Test that you've properly attached an event handler:

$('.element-to-select').click(function() {
  alert('hi');
});