Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Interactive Web Pages with JavaScript Traversing and Manipulating the DOM with JavaScript Perform: Appending and Removing Elements

Danielle Howard
Danielle Howard
4,402 Points

Understanding the purpose of bindTaskEvents

I'm having difficulty understanding what the bindTaskEvents function is doing.

var taskCompleted = function(){
  console.log("Complete task...");  
    //append the task li to the #completed-tasks
  var listItem = this.parentNode
  completedTasksHolder.appendChild(listItem);
  bindTaskEvents(listItem, taskIncomplete);
}

In the code above I understand that completedTasksHolder.appendChild(listItem); moves the list item to the completedTasksHolder, however I can't get my head around what the bindTaskEvents function is doing after this.

var bindTaskEvents = function(taskListItem, checkBoxEventHandler){
  console.log("bind list item events");
  var checkBox = taskListItem.querySelector("input[type=checkbox]");
  var editButton = taskListItem.querySelector("button.edit");
  var deleteButton = taskListItem.querySelector("button.delete");

  editButton.onclick = editTask;

  deleteButton.onclick = deleteTask;

  checkBox.onchange = checkBoxEventHandler;
}

(The rest of my code is the same as Andrews)

4 Answers

Zac Mackey
Zac Mackey
11,392 Points

I think LaVaughn explained this really well, but I also wanted to write up an explanation since I had a bit of trouble understand what is going on here as well:

Firstly, the page loads and your app.js file runs the following code:

var incompleteTasksHolder = document.getElementById('incomplete-tasks'); //ul - #incomplete-tasks
var completedTasksHolder = document.getElementById('completed-tasks'); //   #completed-tasks

This code takes your elements with their respective Id's (incomplete-tasks and completed-tasks) and assigns them to their respective variables (i.e. incompleteTasksHolder and completedTasksHolder).

So now incompleteTasksHolder contains the entirety of the UL element with the Id #incomplete-tasks and vice versa for the completedTasksHolder (it contains the entirety of the UL element with the Id #completed-tasks).

If you open your JavaScript console after loading the page and just type incompleteTasksHolder you'll see the following:

incompleteTasksHolder

      <ul id="incomplete-tasks">
        <li>
          <input type="checkbox">
          <label>Pay Bills</label>
          <input type="text">
          <button class="edit">Edit</button>
          <button class="delete">Delete</button>
        </li>
        <li class="editMode">
          <input type="checkbox">
          <label>Go Shopping</label>
          <input type="text" value="Go Shopping">
          <button class="edit">Edit</button>
          <button class="delete">Delete</button>
        </li>    
      </ul>

So now the incompleteTasksHolder contains all the children of the UL element.

Next, further down in your app.js code you'll remember seeing this part:

//cycle over incompleteTasksHolder ul list items
for(var i = 0; i < incompleteTasksHolder.children.length; i++) {
  //bind events to list item's children (taskCompleted)
  bindTaskEvents(incompleteTasksHolder.children[i], taskCompleted);
}

//cycle over completedTasksHolder ul list items
for(var i = 0; i < completedTasksHolder.children.length; i++) {
  //bind events to list item's children (taskIncomplete)
  bindTaskEvents(completedTasksHolder.children[i], taskIncomplete);
}

What's happening here is, when the page loads this code is ran after everything else (it's at the bottom of your script) and it's a simple for loop. So....

for(var i = 0; i < incompleteTasksHolder.children.length; i++) 

...this portion initiates your loop by stating var i is equal to zero, we're going to check the length of the incompleteTasksHolder.children, and we're going to increment i by 1 so long as it is smaller than the incompleteTasksHolder.children.length.

How do we determine the length of incompleteTasksHolder.children, and what exactly is it? Just type into your JavaScript console and you'll see it's an array!

incompleteTasksHolder.children
['<li>
          <input type="checkbox">
          <label>Pay Bills</label>
          <input type="text">
          <button class="edit">Edit</button>
          <button class="delete">Delete</button>
        </li>',
 '<li class="editMode">
          <input type="checkbox">
          <label>Go Shopping</label>
          <input type="text" value="Go Shopping">
          <button class="edit">Edit</button>
          <button class="delete">Delete</button>
        </li>'   
]

It has two values - the first li element which is for Pay Bills and the second li element which is for Go Shopping.

This means the incompleteTasksHolder.children.length variable is equal to 2. So, the loop will only run twice since we're starting with the i variable at 0, then we'll increment it to 1, then it will stop since after the second time through it will increment to 2, which is NOT less than incompleteTasksHolder.children.length.

The for loop then runs the bindTaskEvents function, passing in the incompleteTasksHolder.children[i] and the taskCompleted function.

bindTaskEvents(incompleteTasksHolder.children[i], taskCompleted);

So basically, each time the loop does this it access the first child of incompleteTasksHolder (it's the li for the Pay Bills line) and the taskCompleted function and passes them into the bindTaskEvents function:

//taskListItem is equal to a value in the incompleteTasksHolder.children array
//checkBoxEvenHandler is equal to the taskCompleted function

var bindTaskEvents = function(taskListItem, checkBoxEventHandler) {
    console.log("Bind List Item events") // Message in console to let you know the function ran
    //select the children and store them in variables next
    var checkbox = taskListItem.querySelector('input[type=checkbox]');
    var editButton = taskListItem.querySelector('button.edit');
    var deleteButton = taskListItem.querySelector('button.delete');

        // bind editTask global function to edit button
        editButton.onclick = editTask;

        // bind deleteTask global function to delete button
        deleteButton.onclick = deleteTask;

        // bind taskCompleted function to checkbox on change(when you click it)
        checkbox.onchange = checkBoxEventHandler; 
                //So now, when you click the checkbox on this event it will trigger the taskCompleted function

}

This function takes those two arguments and does what the notes I've placed in the code explain.

So basically, the bindTaskEvents function assigns the necessary functions to each of the elements in the li. The delete button gets assigned to the deleteTask function, the edit button gets assigned to the editTask function, and the checkbox is assigned to the checkBoxEventHandler, which in this case is the taskCompleted function we passed as an argument.

var taskCompleted = function(){
    console.log('task completed');
    // Append the task list item to the #completed-tasks
    var listItem = this.parentNode;
    completedTasksHolder.appendChild(listItem);
    bindTaskEvents(listItem, taskIncomplete);
}

taskCompleted then runs and your li is changed to a completed task because it is appended to the completedTasksHolder variable (which puts the li and all of it's children into the #completed-tasks ul). And then bindTaskEvents is ran again to ensure the elements are appropriately bound to their functions.

Whew, hope that makes sense. It really helped me to understand everything that's going on with these functions and how they interoperate, so I hope it helps others who read this as well.

LaVaughn Haynes
LaVaughn Haynes
12,397 Points

The list item and an event handler are being passed to the bindTaskEvents function.

bindTaskEvents(listItem, taskIncomplete);

The list item contains all of the buttons and input for that task:

<li>...buttons and inputs here...</li>

That function takes the list item that was passed to it and searches inside of it to find the relevant checkbox, delete, and edit buttons, and assigns them to variables.

var checkBox = taskListItem.querySelector("input[type=checkbox]");

Finally it assigns new event handlers to those buttons and input. In this case the checkbox is assigned the "taskIncomplete" event handler for the onchange event because "taskIncomplete" was passed to the function as "checkBoxEventHandler".

checkBox.onchange = checkBoxEventHandler;

I hope that makes sense.

He talks about it at the 3 minute mark on this video https://teamtreehouse.com/library/interactive-web-pages-with-javascript/traversing-and-manipulating-the-dom-with-javascript/perform-traversing-elements-with-children

where in the video is bindTaskElements discussed?

Danielle Howard
Danielle Howard
4,402 Points

Thank you Zac, this explains it really well.

Danielle Howard
Danielle Howard
4,402 Points

Thank you Zac, this explains it really well.