Step 4

Exercises

  • Write a list of friends using an array and innerHTML. Solution
  • Write a list of friends with a remove button. Solution
  • Write a list of friends using an unsigned list element. Solution
  • Write a list of friends using a kind of template. Solution

Removing items

The next step will be adding a button to our task item for taking it off of our list.

Let's look at the code of our items:

4.1 — The current markup of each task item

  <li>Buy coffee</li>

Very simple markup; the only thing it contains is the text of the task.

Now we need something like this:

4.2 — The markup we need for adding the remove feature

<li>
  <span>Buy coffee</span>
  <button onclick="removeItem(event)">×</button>
</li>

Here our <li> element contains two children elements: a <span> with some text, and a <button> that we will use to remove the task.

Let’s take a look at how we are currently rendering the task item inside the updateList() function:

4.3 — The code we’re currently using to render the task item element

listElement.innerHTML += '<li>' + item + '</li>';

We are doing something very simple to obtain a new task item element; we are connecting three strings: <li>, the item text and </li>. As we saw above, now we need something slightly more complex; it's time to move this rendering logic into a new function:

4.4 — A simple renderItem() function

var renderItem = function(itemText) {
  return '<li>' + itemText + '</li>';
}

4.5 — Let’s change the code inside updateList() to use the new renderItem() function

listElement.innerHTML += renderItem(item);

The end result hasn't change yet, but we have built a dedicated place that will contain all the logic we need for transforming an input (the task item text) to an output (the task item HTML markup).

Now we have some work to do inside the renderItem() function. We have to change our output from what we have now (see 4.1) to what we need (see 4.2). To do this, we will leverage a widely used concept in HTML programming: the templates.

Key concepts: templates and placeholders

A template is a generic piece of HTML code that contains placeholder strings. Replacing placeholder strings with real values is called “compiling a template”.

Let’s create our first template. Before the </head> tag, let’s add this markup:

4.6 — The task item template element

<script id="item-template" type="text/html">
  <li>
    <span>_TEXT_</span>
    <button onclick="removeItem(event)">×</button>
  </li>
</script>

We added a <script> element (with type text/html) that we are using just for storing some HTML (our template string). This element is not displayed in our page (because <script> elements are never displayed) and the browser will not try to run the code inside it because the type we specified is not executable. Just think of this element as a “container of text”.

The template HTML contains a placeholder string (_TEXT_) that we will replace with real value shortly.

Now let’s go back to our renderItem() function. What we are going to do now is replacing the _TEXT_ placeholder with our task item text. To do this, we will use the replace() method available on strings.

4.7 — renderItem() with template

var renderItem = function(itemText) {
  var template = document.querySelector('#item-template').innerHTML;
  return template.replace('_TEXT_', itemText);
}

Cool! Now the markup of each element is changed and a little button is shown next to each of our task items. But if we click on that button, nothing happens. Let’s fix this!

We need to create a removeItem() function (as we wrote in the onclick attribute of our button), that will:

  • remove a task from our list
  • update the task list

To do this, we will use the array's filter() method to update our listItems. Let’s look at the function code:

4.8 — the removeItem() function

var removeItem = function(event) {
  var clickedItemText = event.target.previousElementSibling.innerHTML;

  listItems = listItems.filter(function(itemText) {
    return clickedItemText != itemText;
  });

  updateList(listItems);
}

The first thing to notice here is that the removeItem() function does not automatically know which task we want to remove. For JavaScript, what happened is that a button received a click. What happens from now on is completely in our hands.

The way we get to the clicked task text is by accessing the event object, which is generated by the browser each time a user is interacting with an element (eg. clicking on it). In the event object we can find a lot of useful information. For example, the element that received the click (called target: our button).

If we look at the markup, we notice that our button is at the same level of our span, and comes right after it; they are siblings. We want to reach the previous element sibling of the button and read its inner HTML. We do this by calling previousElementSibling on the event.target.

After, we use the filter() method on our listItems array to obtain a new copy of our list that does not contain the task we are removing.

The last step of our removeItem() function will be displaying this new list by calling updateList().

What we just did:

  • we created a template for our task items
  • we added a button on each task item with an onclick attribute
  • we created a function that removes the clicked item by filtering our listItems array

Break: Let’s add some style!

Time has finally come to make our little app look less ugly!

We will not be adding new features during this step but our app will look much better after some small changes in the markup and after we link our stylesheet. We will not go through the CSS rules – CSS is a whole new argument and it is beyond the scope of today’s workshop, but feel free to ask your coach for resources if you are interested.

Let’s create a <link> tag that points to our CSS file: place this code after the <title> tag, inside <head>.

<link href="styles/app.css" rel="stylesheet">

If we reload the page we notice that our page has now a background image and the text changed a bit.

Now let's turn our template item into this:

<li class="list-group-item">
  <span>_TEXT_</span>
  <button onclick="removeItem(event)" class="close">×</button>
</li>

And between the app title <h2> and the <script> tag let's replace everything with this:

<div class="panel panel-default">
  <form onsubmit="createNew(event)" class="panel-heading">
    <input id="new-item" class="form-control" placeholder="Add a new task..." autofocus="autofocus">
    <button class="btn btn-primary">Add</button>
  </form>
  <ul id="task-list" class="list-group"></ul>
</div>

Notice that we preserved all the previous elements and we added some more elements and classes. This way we gave a better structure to our markup. We created a box that contains our tasks list, plus a dedicated box that contains our input field.