Lesson Monday

In the last lesson we used event delegation to target dynamically created li elements. Next, let's create code to show a contact's detail info. The area to display a contact's details is already present in our index.html. Here's the current code:

index.html
...
<div id="show-contact">
  <p>First Name: <span class="first-name"></span></p>
  <p>Last Name: <span class="last-name"></span></p>
  <p>Phone Number: <span class="phone-number"></span></p>
  <div id="buttons">
  </div>
</div>
...

However, we don't see this HTML in the DOM because of this code in our CSS:

css/styles.css
#show-contact {
  display: none;
}

Our next step is to update the attachContactListeners() function so it actually calls a function instead of just running console.log(). Here's the updated function:

scripts.js
...

function attachContactListeners() {
  $("ul#contacts").on("click", "li", function() {
    showContact(this.id);     // <--- This is new!
  });
}

...

All we've done here is remove console.log("The id of this <li> is " + this.id + "."); and replace it with showContact(this.id);.

We haven't written a showContact() function yet. However, if we think about our attachContactListeners() function, it doesn't make sense to actually have code for showing a contact here. All this function should care about is attaching event listeners to contacts. By doing this, we are already making our code more modular and separating out concerns - attachContactListeners() is only concerned with attaching listeners while showContact() will be concerned with the actual details of showing a contact.

Of course, if we were to run this in the browser now, our code wouldn't work. We have to actually write a showContact() function first!

  • In the code snippet above, this in showContact(this.id); refers to the li in the on() method. "click" is the event our handler is listening for while the "li" is the actual element that the handler is attached to. Previously, our console.log() correctly grabbed the ID of an li. On top of that, the li will always correspond to the actual ID of the contact because our displayContactDetails() function establishes the following: "<li id=" + contact.id + ">".

Let's actually write the code for our showContact() function to handle this now. We'll define it above attachContactListeners() and below displayContactDetails():

scripts.js
...

function showContact(contactId) {
  const contact = addressBook.findContact(contactId);
  $("#show-contact").show();
  $(".first-name").html(contact.firstName);
  $(".last-name").html(contact.lastName);
  $(".phone-number").html(contact.phoneNumber);
  let buttons = $("#buttons");
  buttons.empty();
  buttons.append("<button class='deleteButton' id=" +  + contact.id + ">Delete</button>");
}

...
  • We start by utilizing our findContact() method. Remember that we are cheating a bit and that addressBook is global in scope, which is why we can use it here.

  • We show the hidden #show-contact content with the contact's full information. Note that we aren't being incredibly efficient here - three queries to the DOM with the jQuery's html() method. If you wish, you can refactor this code later to make only one query. To do so, you simply need to remove the HTML from index.html and construct it in this method instead.

  • Notice we save our "#buttons" selector in a variable. We will save a little overhead as we don't need to query the DOM to find the selector - however, we will still be querying the DOM twice, once to call empty() and once for appending a button.

Now our application will be working correctly again. We can click on an <li> and see that contact's detail info appear in the DOM!

Delete Functionality

Notice the code above also appends a button to delete the contact being displayed. Let's add this functionality next. We'll modify attachContactListeners() and use event delegation again:

scripts.js
...

function attachContactListeners() {
  $("ul#contacts").on("click", "li", function() {
    showContact(this.id);
  });
  // Code below here is new!
  $("#buttons").on("click", ".deleteButton", function() {
    addressBook.deleteContact(this.id);
    $("#show-contact").hide();
    displayContactDetails(addressBook);
  });
}

...

Now when a user clicks on the delete button, the contact will be deleted. The contact detail will be hidden (since the contact has been deleted) and we'll call displayContactDetails(addressBook) to refresh the list of contacts. We could refactor this code into a separate function as well, but we'll leave that to you.

More Improvements

Our address book is now fully functional. Let's add a few more user experience improvements before wrapping up.

Bootstrap Styles

First, let's utilize Bootstrap classes to add styling and a more organized layout to our page. This should be review:

index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="css/styles.css" rel="stylesheet" type="text/css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <script src="js/scripts.js"></script>
  <title>Address Book</title>
  <title></title>
</head>
<body>
  <div class="container">
    <h1>Address Book</h1>
    <div class="row">
      <div class="col-md-6">
        <hr>
        <h2>Add a Contact:</h2>
        <form id="new-contact">
          <div class="form-group">
            <label for="new-first-name">First Name</label>
            <input type="text"  class="form-control" id="new-first-name">
          </div>
          <div class="form-group">
            <label for="new-last-name">Last Name</label>
            <input type="text"  class="form-control" id="new-last-name">
          </div>
          <div class="form-group">
            <label for="new-phone-number">Phone Number</label>
            <input type="text"  class="form-control" id="new-phone-number">
          </div>
          <button type="submit" class="btn-primary">Add</button>
        </form>
        <hr>
        <h2>Contacts:</h2>
        <ul id="contacts">
        </ul>
      </div>
      <div class="col-md-6">
        <div id="show-contact">
          <p>First Name: <span class="first-name"></span></p>
          <p>Last Name: <span class="last-name"></span></p>
          <p>Phone Number: <span class="phone-number"></span></p>
          <div id="buttons">
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>

Our page will look a little nicer!

Empty Form Fields

Let's also make sure to empty out our form fields after submission:

scripts.js
...
$(document).ready(function() {
  attachContactListeners();
  $("form#new-contact").submit(function(event) {
    event.preventDefault();
    const inputtedFirstName = $("input#new-first-name").val();
    const inputtedLastName = $("input#new-last-name").val();
    const inputtedPhoneNumber = $("input#new-phone-number").val();

    // The next three lines are new:
    $("input#new-first-name").val("");
    $("input#new-last-name").val("");
    $("input#new-phone-number").val("");

    let newContact = new Contact(inputtedFirstName, inputtedLastName, inputtedPhoneNumber);
    addressBook.addContact(newContact);
    displayContactDetails(addressBook);
  });
});

If you want to see all the updated code, check the branch in the repository below.


Example GitHub Repo for the Address Book

Examples


Create a UI function to show a contact in the DOM:

function showContact(id) {
  const contact = addressBook.findContact(id);
  $("#show-contact").show();
  $(".first-name").html(contact.firstName);
  $(".last-name").html(contact.lastName);
  $(".address").html(contact.phoneNumber);
  let buttons = $("#buttons");
  buttons.empty();
  buttons.append("<button class='deleteButton' id=" +  + contact.id + ">Delete</button>");
}

Example GitHub Repo for the Address Book

Lesson 20 of 32
Last updated April 6, 2021