Harry Cresswell

Practical JavaScript – Escape the Console

In version 9 of our todo app we will completely escape the console, allowing users to not only use the interface but see the data returned aswell.

Requirements

  • There should be an li element for every todo
  • Each li element should contain .todoText
  • Each li element should show .completed

List items will represent every todo in our list. Each list item should contain the .todoText property and show if it’s .completed or not.

We will also remove the display todos buttons as we want the data to automatically display in the interface when actions are taken by the user.

Inserting li elements into the DOM

We want to repesent our todo list in the form of an unordered list in html. Where each li represents our todo list items.

<ul>
  <li></li>
  <li></li>
  <li></li>
</ul>

With our app we won’t know how many items are in our list so we need to be able to dynamically add and remove these li’s. So the first thing to understand is how we can insert list items into the DOM. We can do this using JavaScript.

We can start in our HTML by creating an empty ul.

<ul>
</ul>

Then, inside the console first we can initilize a new varibale, which uses a method on the document object called createElement to create an li.

var todoLi = document.createElement('li');

Now if you type todoLi and hit enter, you can see we have an empty li to work with.

<li></li>

Now we have something we can insert into the DOM, next we need to figure out a way we can insert this list item into the ul. So we need to create a referece to the ul in our HTML file.

To do this we can create another variable and use the querySelector method on the document object to grab the ul element.

var todosUL = document.querySelector('ul');

Now if we type todoUL and hit enter we can see the unordered list on our page is returned. If you hover over the ul in the console you will see where it is highlighted on the page.

Now we have a referece to our todos ul we can add the list item to the ul. Because the li is a child element of the parent ul we can use a method called appendChild to do this.

todosUL.appendChild(todoLi);

Run todosUL and we can see our li has been added to the page inside the ul element. Again we can hove over <li></li> in the console and see the <li> highlighted in the page view.

This is the technique we’ll be using to add our list items to the page. To wrap up we need to do 3 things:

  • create an <li> element and save it to a varibale called todoLi
  • grab a reference to the <ul></ul> on our page and assign it to a variable called todosUL
  • Insert the <li> into our <ul></ul> using appendChild

There should be an li element for every todos

Now we can take what we’ve learnt and build a function, which runs the code everytime theres a new item added to the todos.

First we need the empty <ul></ul> in our HTML.

Next, inside our JavaScript file we can create a new object called view. The reason it’s called view is because it’s responsible for what the user sees. I doen’t have any logic or change any data in our app, it just takes the todos array and displays it in the page. It’s responsibe only for view functions.

var view = {

}

Next we need to create a method in our view object for displayTodos which is equal to a function. Eventually this will replace the todoList.displayTods function as we will no longer need to display todos to the console.

var view = {
  displayTodos: function() {

  }
}

Inside the function the code will be similar to what we learnt in the last section. First we grab a reference to the todos ul using the querySelector method. Then we want to create a new li item to contain our todo items. We can do this with the createElement method and tell it we want to create a li. Next we need to grab the todosUl and insert the li into it with appendChild.

var view = {
  displayTodos: function() {
    var todosUl = document.querySelector('ul');
    var todoLi = document.createElement('li');
    todosUL.appendChild(todoLi);
  }
}

Now we can run our code with view.displayTodos to see how it works. You can see a <li> is added to the page. Run the code again and you’ll see another item added.

Now we want this code run for each item in our todos array. We can do this with a for loop, which runs our code for each of the items in our todos array. So if our array has 5 items it will run 5 times, and so on.

var view = {
  displayTodos: function() {
    for (var i = 0; i < todoList.todos.length; i++) {
      var todosUl = document.querySelector('ul');
      var todoLi = document.createElement('li');
      todosUL.appendChild(todoLi);
    }
  }
}

If we run the app we can see how this works. First we will need to add a todo item, then we can type view.displayTodos and hit enter. Run the code twice and you’ll see there are now 2 <li>’s on the page, even though we only have 1 todo item. This isn’t exactly how we want the app to work. We want displayTodos to always reflect the number of items in our todos array.

Ideally we could clear the previous items from the page before the code runs, so each time it starts from 0.

We can do this by moving todosUl outside of the for loop as we don’t need to create it everytime the for loop runs. Then using the .innerHTML method to target whatever is inside the <ul>, we can set it to nothing with an empty string. This will clear out the <ul> before it starts adding new <li> elements.

var view = {
  displayTodos: function() {
    var todosUl = document.querySelector('ul');
    todosUl.innerHTML = '';

    for (var i = 0; i < todoList.todos.length; i++) {
      var todoLi = document.createElement('li');
      todosUL.appendChild(todoLi);
    }
  }
}

To test this is working we can head to our interface and add 3 new items. Next run view.displayTodos();.

We should now see 3 new <li>’s on the screen. Now when we run the code again, instead of adding another 3 <li>’s to make 6 — as it did before – it clears the 3 and adds another 3. So now it looks like nothing changes.

Now our code reflects the number of items in our todos array, exactly as we want it to.

Each li element should contain .todoText

The next requirement is to add the actual todo text to each <li> element. So we can show something to the user.

We can do this in one line. First using .textContent, a property built into todoLi, which allows you to set the text content of an element. In this case our <li> elements.

todoLi.textContent

Now we need to give it the value of our todo text. To make this happen we need to access our todos array.

todoLi.textContent = todoList.todos

But now we need to access the actual todo text for each item in our todos array. To access each item we can use bracket notation [] and parse in i, as this will allow us to look at each item through our for loop. Finally we can use .todoText to grab the actual todo text for each item and add it as the content inside each <li>.

todoLi.textContent = todoList.todos[i].todoText;

The final code should look like this.

var view = {
  displayTodos: function() {
    var todosUl = document.querySelector('ul');
    todosUl.innerHTML = '';

    for (var i = 0; i < todoList.todos.length; i++) {
      var todoLi = document.createElement('li');
      todoLi.textContent = todoList.todos[i].todoText;
      todosUL.appendChild(todoLi);
    }
  }
}

Now if we add a new todo item to our todo list, then run view.displayTodos(); we should see that todo item output to the screen.

So to recap, in this line we are taking the todoLi element, accessing the todoLi element’s textContent property and setting it to the todoText property each of our todo objects. In other words we’re changing the value of textContent to the todoText property of each of our todo items in our todos array.

Each li element should show .completed

Now all of our todo items are showing but they don’t yet show the completed property. Next we need to show which items are completed and which are still to finish.

We can write some comments in our code to plan this out. To start we can set up a variable to store the value of our todoText, but this time with completion data included. We can use an empty string for now as we will update this later.

// var todoTextWithCompletion = '';

Using an if statement we can set a condition that checks whether each todo completed, when the condition is true, at which point it will return (x) todoText. If the condition is false we can use an else statement to return ( ) todoText.

// var todoTextWithCompletion = '';
// if (todo.completed === true)
  // (x) todoText
// else
  // ( ) todoText

Once the todoTextWithCompletion variable is set–based on the if statement–we want to get the todoLi and set it equal to the new value of todoTextWithCompletion.

// var todoTextWithCompletion = '';
// if (todo.completed === true)
  // (x) todoText
// else
  // ( ) todoText
// todoLi.textContent = todoTextWithCompletion;

Now we have our comments we can use them to guide our code. To start we can set up the todoTextWithCompletion varibale. Eventually we will use this to store the completed value but for now we can set it to nothing. Next we need an if statement which checks whether todo.completed is true.

var todoTextWithCompletion = '';
if () {

} else {

}

Before we set the condition we need to create a reference to todo. We can store this referece in new variable which gives us access to each todo item. This isn’t 100% necessary but it will save us having to write out the code each time.

var todoTextWithCompletion = '';
var todo = todoList.todos[i];
if () {

} else {

}

Now we have a reference we can add the condition to our if statement. Then update todoTextWithCompletion if the condition is met. We can use the plus operator + to combine the completion string -(x) or ( )- with the todoText string.

var todoTextWithCompletion = '';
var todo = todoList.todos[i];
if (todo.completed === true) {
  todoTextWithCompletion = '(x) ' + todos.todoText;
} else {

}

The only difference in the else statement is we’re removing the (x) in favor of an empty set of parentheses.

var todoTextWithCompletion = '';
var todo = todoList.todos[i];

if (todo.completed === true) {
  todoTextWithCompletion = '(x) ' + todo.todoText;
} else {
  todoTextWithCompletion = '( ) ' + todo.todoText;
}

The final step is to set textContent on our <li>’s equal to the value of todoTextWithCompletion.

var todoTextWithCompletion = '';
var todo = todoList.todos[i];

if (todo.completed === true) {
  todoTextWithCompletion = '(x) ' + todo.todoText;
} else {
  todoTextWithCompletion = '( ) ' + todo.todoText;
}

todoLi.textContent = todoTextWithCompletion;

As textContent now contains the completion data we can remove the line below our code which shows our todo item. without the completion data.

todoLi.textContent = todoList.todos[i].todoText;

Now we can test our code out to see if it’s working. Add a new todo then run view.displayTodos();. You should see your new item show on the page with empty parentheses. Hit the toggle Completed button then run view.displayTodos(); again and you should see an x inside the parentheses.

Escaping the console

To escape the console completed we still need to find another way to run view.displaytodos();, so we don’t have to open the console each and every time we need to run the method.

We really only need to display todos any time the data changes. That means anytime one of the buttons on the page is clicked by the user. We can do this by adding view.displayTodos(); at the end of each method in our handlers object.

Now that our todos display on the page when you click any of the buttons, our display todos button is redundant. So we can remove the display todos button code inside our HTML. As we no longer have a button, our displayTodos handler is also useless, so we can remove this too.

We’re now displaying our todos directly to the DOM, so there’s also no need for this.displayTodos(); on the methods inside the todoList object. As all this does is display the todos to the console. We can remove this line of code from each of the methods. Finally we can remove the entire displayTodos method from todoList as we’re now doing this in the DOM, via our new view object.

Now view is the only part of our app concerned with displaying information. That’s why it’s called a view object.

Review v.9

Version 9 mostly involved DOM manipulation, which we did inside a new object called view. This displayTodos method is similar to our old one, the only differnce is we’re no longer working with the console but the DOM. So that’s why we needed to learn about creating and modifying DOM elements.

In our HTML we added an unordered list element and removed the display todos button.

We’re now using the view.displayTodos method inside every handler because each of the handlers can change data. That means when data changes we want to update what we show to the user.

Finally, we refactored our todoList object, taking away the displayTodos method and the method calls associated with that method.

Now todoList has a very specific purpose; it represents the data and has methods which change the data. Now we have all related methods on a single object. This pattern is the same for our other objects. We’re using handlers specifically for user interactions and view for showing users what the todo list looks like. All our code is organised into related methods inside objects which has a specific purpose.

Weekly Newsletter

Brain—tactics is a weekly roundup of articles, tools and tips for product designers and front-end developers. I send it every Friday morning.