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 Perfect

Andrey Konovalenko
Andrey Konovalenko
9,409 Points

Solution for Interactive Web Pages with JavaScript - Perfect 1 empty task - solved; 2 Save button - added.

Hi! For solving the issue with empty task, I've used an if else statement.

var addTask = function() {
    console.log("AddTask...");
    //Create a new list intem with the text form #new-task:
    var emptyTask = "";
    if (taskInput.value !==emptyTask) {

        var listItem = createNewTaskElement(taskInput.value);

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

        taskInput.value = "";
    } else {
        console.log("No task was appended");
    }

}

To change edit button to "Save" button I've used querySelector("button.edit") in editTask function

var editTask = function() {
    console.log("editTask...");
    var listItem = this.parentNode;
    var editInput = listItem.querySelector("input[type=text]");
    var label = listItem.querySelector("label");
    var containsClass = listItem.classList.contains("editMode");
    var button = listItem.querySelector("button.edit");
    console.log(button.innerText);
    //if the class of the perent is .edidMode
    if(containsClass) {
        //Switch from .editMode
        //Label text become the input's value
        label.innerText = editInput.value;
        button.innerText = "Edit";

    } else {
    //else
        //Switch to .editMode
        //input value becomes to label's 
        editInput.value = label.innerText;
        button.innerText = "Save";

    }
    //Toggle .editMode on the liet item     
    listItem.classList.toggle("editMode");

}

Since the function is also being performed on the button for edit I think you can also use

this.innerText = "Save";

one other thing is adding this line is redundant:

var emptyTask = "";

Instead, you can modify this line:

if (taskInput.value !==emptyTask)

to become:

if (taskInput.value !== "")

Also, if you wanted to do the toggle between Save and Edit button like in your original post, you don't need that "button.edit" that's in your variable 'button', it works just as "button". Sorry if this was confusing, I was just a bit happy I solved this as well!

3 Answers

Andrey Konovalenko
Andrey Konovalenko
9,409 Points

Thank you. I understood what did you mean. In my case selecting button.edit in editTask was redundant because we have already did It in bindTaskEvents function. Now I realize why you suggested me to use this global object.

Here is editTask function with your suggestion I commented my variant of code and It became bit shorter and without repeating.

var editTask = function() {
    console.log("editTask...");
    var listItem = this.parentNode;
    var editInput = listItem.querySelector("input[type=text]");
    var label = listItem.querySelector("label");
    var containsClass = listItem.classList.contains("editMode");
/*  var button = listItem.querySelector("button.edit");
    console.log(button.innerText);*/
    //if the class of the parent is .edidMode
    if(containsClass) {
        //Switch from .editMode
        //Label text become the input's value
        label.innerText = editInput.value;
        this.innerText = "Edit";

    } else {
    //else
        //Switch to .editMode
        //input value becomes to label's 
        editInput.value = label.innerText;
        this.innerText = "Save";

    }
    //Toggle .editMode on the liet item     
    listItem.classList.toggle("editMode");

}

Better Solution to Warn the user When they try to create an Empty Task shows a warning Message in Red

<script>
//Add new task
var addTask = function(){
console.log("Add Task");
var parent =  this.parentNode;

 if (taskInput.value === "" ){
   console.log("Empty");
   var warn = document.createElement("p");
   warn.innerText = "Can't create an Empty Task";
   warn.style.color = "red";
   parent.appendChild(warn);
 }
 else{
    //Create a new list of the item with the text from #new-item:
 var listItem = createNewTaskElement(taskInput.value);
 //Append list Item to the Incomplete taskHolder
incompleteTasksHolder.appendChild(listItem);
 bindTaskEvents(listItem, completedTask); 
  taskInput.value = "";
  var check = parent.querySelector("p");
   if (check){
      parent.removeChild(check);
    }
  }
}
</script>

For Edit and Save Button to Switch Edit and Save when you finish editing

<script>
//Edit Existing task
var editTask = function(){
  console.log("Edit Task");
  //If the class of the parent is .editMode
 var listItem = this.parentNode;
 var editInput = listItem.querySelector("input[type=text]");
 var label = listItem.querySelector("label");
 var containsClass = listItem.classList.contains("editMode");
 var editButton = listItem.querySelector("button.edit");

  if (containsClass){
    //Switch from .editMode
    //Label text becomes the input value
    label.innerText = editInput.value;
    editButton.innerText = "Edit";
  }
  else{
     //Switch to .editMode 
    //Input value becomes the labels text
    editInput.value = label.innerText ;
    editButton.innerText = "Save";

  }
  //toggle editmode on the list item
  listItem.classList.toggle("editMode");
}
</script>
Andrey Konovalenko
Andrey Konovalenko
9,409 Points

Thanks for answer) I'm not familiar with .this statement yet. I checked your advice, it works. But I don't really understand why?

Where the connection between button.edit and this.innerText
Not only button have innerText. How can we know that we change particular innerText in button when we use .this statement?

When i use button.innerText i have 100% confidence that i chose particular button.

To be honest I just did this lesson for the second time and for some reason it finally clicked for me. I would agree that using button.innerText probably assures for more piece of mind to know exactly what you are selecting and I understand why you would use that. I just was able to put together that the editTask function was directly tied to the editButton back in the bindTaskEvents function any code we entered would affect the Edit button tags in the html. Sorry I can't really explain it better as of yet.

When the event handler for edit button is called, an object is passed implicitly to the function/eventhandler. The object passed is the HTML element upon which the triggering action occurred in this case it was the mouse click and the element was the editbutton

Now the object which was passed implicitly to the eventhandler is referenced by the 'this' variable , that is the 'this' variable points to the implicilty passed object .

Since in our case the edit button is the object passed implicitly to the editTask eventHandler, the 'this' reference variable will always point to the edit button.

Hopes this clears your doubt.