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 trialBrent Colson
2,174 PointsUncaught 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
28,690 PointsIn 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
18,814 PointsThe 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!