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: Modifying Elements

Brent Colson
Brent Colson
2,174 Points

Uncaught TypeError: Cannot set property 'onclick' of null - bindTaskEvents @ app.js:117, and addTask @ app.js:61

Hi, I've been trying to figure this out for a while - but I am not able to figure out what is going on. When i click on the "add" button, I am getting an error with that seems to be related to the editButton.onclick = editTask; line of code. Any thoughts?

Thanks!

/***************************
Variables
***************************/

var taskInput = document.getElementById("new-task"); //new-task
var addButton = document.getElementsByTagName("button")[0];//first button
var incompleteTasksHolder = document.getElementById("incomplete-tasks"); //incomplete-tasks
var completedTasksHolder = document.getElementById("completed-tasks");  //completed-tasks



/***************************
Functions
***************************/

//New Task List Item
var createNewTaskElement = function(taskString){
  //createlist item
  var listItem = document.createElement("li"); 
  //input (checkbox)
  var checkBox = document.createElement("input"); // checkbox
  //label
  var label = document.createElement("label");
  //input (text)
  var editInput = document.createElement("input"); // text
  //button.edit
  var editButton = document.createElement("button"); // edit
  //button.delete
  var deleteButton = document.createElement("button"); // delete

  //Each element need modifying
  checkBox.type = "checkbox";
  editInput.type = "text";
  editButton.innerText = "Edit";
  editButton.className = "edit";
  editButton.innerText = "Delete";
  editButton.className = "delete";
  label.innerText = taskString

  //Each element needs appending
  listItem.appendChild(checkBox);
  listItem.appendChild(label);
  listItem.appendChild(editInput);
  listItem.appendChild(editButton);
  listItem.appendChild(deleteButton);
  return listItem
}

//Add a new task
var addTask = function() {
  console.log("Add task...");
  //Create a list item with the text from #new-task
  var listItem = createNewTaskElement(taskInput.value);

  //Append listItem to incompleteTasksHolder
  incompleteTasksHolder.appendChild(listItem);
  bindTaskEvents(listItem, taskCompleted);
}

//Edit an existing task
var editTask = function() {  //this is an eventHandler
  console.log("Edit task...");
  //When the edit button is pressed
    //Switch from .editMode
    //label text becomes the input's value
  //else
    //Switch to .editMode
    //input value becomes the label's text

  //toggle .editMode on the parent
}

var deleteTask = function() {  //this is an eventHandler
  console.log("Delete task...");
  var listItem = this.parentNode;
  var ul = listItem.parentNode

  //Remove hte parent list  item from the ul
  ul.removeChild(listItem);
}

var taskCompleted = function() { //this is an eventHandler
  console.log("Task complete...");
  //Append the text list item to the #completed-tasks
  var listItem = this.parentNode;
  completedTasksHolder.appendChild(listItem);
  bindTaskEvents(listItem, taskIncomplete);
}

var taskIncomplete = function() { //this is an eventHandler
  console.log("Task incomplete...");
  //Append the task list item to the #incomplete-tasks
  var listItem = this.parentNode;
  incompleteTasksHolder.appendChild(listItem);
  bindTaskEvents(listItem, taskCompleted);
}



/***************************
Bind Events
***************************/

var bindTaskEvents= function(taskListItem, checkBoxEventHandler) { /*this process is repeated to cycle over incompleteTaskHolder and completedTaskHolder so we created a function (DRY) */
  console.log("Bind list item"); 

  //select taskListItem children
  var checkBox = taskListItem.querySelector("input[type=checkbox]");
  var editButton = taskListItem.querySelector("button.edit");
  var deleteButton = taskListItem.querySelector("button.delete");;

    //bind editTask to edit button
    editButton.onclick = editTask;  
    //bind deleteTask to delete button
    deleteButton.onclick = deleteTask;

    //bind checkBoxEventHandler to checkbox
    checkBox.onchange = checkBoxEventHandler;
}

//Set the click handler to the addTask function
addButton.onclick = addTask; 

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

2 Answers

Sean T. Unwin
Sean T. Unwin
28,690 Points

In the line above editButton.onclick = editTask; there are two (2) semi-colons at the end of the line:

var deleteButton = taskListItem.querySelector("button.delete");; //<-- extra semi-colon

Edit:

Furthermore, and this is to the root of the issue I believe, is that inside bindTaskEvents() there is a variable created:

var editButton = taskListItem.querySelector("button.edit");

Yet there is no element with that criteria that exists because, as pointed out by rydavim, the editButton has been over-ridden from a typo for the deleteButton within createNewTaskElement() as so:

editButton.innerText = "Edit";
editButton.className = "edit";
editButton.innerText = "Delete";
editButton.className = "delete";

As a result of this there is no taskListItem.querySelector("button.edit"); to bind a click event to.

rydavim
rydavim
18,813 Points

The first thing is that you seem to be attempting to modify editButton attributes multiple times. Perhaps you inteded some of those lines to be for the deleteButton?

  //Each element need modifying
  checkBox.type = "checkbox";
  editInput.type = "text";
  editButton.innerText = "Edit";
  editButton.className = "edit";
  editButton.innerText = "Delete"; // This is still trying to modify editButton.
  editButton.className = "delete"; // You probably mean deleteButton?
  label.innerText = taskString

The second thing is that I think none of this is working because I'm not sure that you can get and set things that haven't been created yet. For example, I'm not sure you can set className if there isn't already one or more classes associated with that element. I could be wrong about this!

I know for sure that setAttribute works for all of these attributes, and is able to create them if they don't already exist. You may also need to use textContent instead of innerText.

  checkBox.setAttribute("type", "checkbox");

  label.textContent = taskString;

  editInput.setAttribute("type", "text");
  editInput.setAttribute("value", taskString);

  editButton.setAttribute("class", "edit");
  editButton.textContent = "Edit";

  deleteButton.setAttribute("class", "delete");
  deleteButton.textContent = "Delete";

Hopefully that's helpful. Please let me know if you have any questions!