Lesson Tuesday

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 lang="en-US">
<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-3.5.1.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.

Note: THE CODE BELOW IS TRICKY. WE JUST WANT YOU TO KNOW THE STUFF ABOVE THIS POINT.

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

(Note that we are using jQuery's css method to illustrate a point here. It's generally not a good idea to mix CSS with JavaScript.)

The code in the snippet above 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');
  });
});

If we click Say "hello" and then the message, we'll get our alert.

However, there's a problem: if we click the button twice, all of the code inside of the click() function will be triggered again. Another click handler will be attached to the li elements that are already on the page.

That means the second time we click the Say "hello" button, the pre-existing li elements will have two click handlers that pop up a "hi" alert. The new li elements will have only one because they didn't exist yet when the click handler was first added to all li elements.

The third time we click the Say "hello" button, yet another click handler will be attached to the elements already there. You can see where this is going - some of our li elements will now have three click handlers attached to them. When a user clicks them, the "hi" alert will pop up three times.

We can fix this by only attaching handlers to text we've just inserted. Since we're inserting our messages 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 we'll cover it more in a few weeks. 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');
});

Lesson 38 of 61
Last updated July 30, 2020