1

I have created a todo-list following watchandcode.com course and I tried to add more functionality to it. So I want to make the COMPLETED button which toggles the non-completed list items ad completed,DISAPPEAR after I click on it and after it turns the list item into completed. I hope I expressed my self properly of what I'd like to achieve. Here's the code down below, thanks in advance! I copied the whole code down below, but if you only need to see the parts that I might be referring (I am not sure, as I am really new into JS), here they are:

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");
        var todo = todoList.todos[i];
        var todoTextForCompleted = "";
        if (todo.completed === true){
            todoTextForCompleted = todo.todoText;
            todoLi.classList.toggle("checked");
        } else {
            todoTextForCompleted = todo.todoText;
            todoLi.classList.toggle("notCompleted");
        }
        todoLi.id = i;
        todoLi.textContent = todoTextForCompleted;
        todoLi.appendChild(this.createDeleteButton());
        todoLi.appendChild(this.createToggleButton());
        todosUl.appendChild(todoLi);
    }
},
createToggleButton: function(){
    var toggleButton = document.createElement("button");
    toggleButton.textContent = "Completed";
    toggleButton.id= "toggleBtn";
    return toggleButton;
},

Here's the whole code if its necessary:

    //The object that holds the todo list:
var todoList =  {
    todos: [],
    //Method for ADDING a TODO items
    addTodo: function(todoText){
        this.todos.push({
            todoText: todoText,
            completed: false,
        });
    },
    //Method for changing the made TODO items
    changeTodo: function(position, newValue){
        this.todos[position].todoText = newValue;
    },
    //Method for deleting TODO items:
    deleteTodo: function(position){
        this.todos.splice(position, 1);
    },
    //Method for toggling a todo item as COMPLETED:
    toggleCompleted: function(position){
        var todo = this.todos[position];
        todo.completed = !todo.completed;
    },
    //Method for toggling ALL todo items:
    toggleAll: function(){
        var totalTodos = this.todos.length;
        //Counting the ones that are completed
        var completedTodos = 0;

        //Getting the number of the completed todos:
        for(var i = 0; i < totalTodos; i++){
            if(this.todos[i].completed === true) {
                //For every todo item that is completed, increase completedTodos by 1;
                completedTodos++;
            }
        }

        //Case 1: If everything is true, the turn it into FALSE;
        if(completedTodos === totalTodos){
            for(var i = 0; i < totalTodos; i++){
                this.todos[i].completed = false
            }
                //Case 2: Otherwise, make everything TRUE;
            } else {
                for (var i = 0; i < totalTodos; i++){
                    this.todos[i].completed = true;
                };
            };
        }
}

//Object for the BUTTON HANDLERS:
var handlers = {
    addTodo: function(){
        var addTodoText = document.getElementById("addTodoTextInput");

        todoList.addTodo(addTodoTextInput.value);
        //Clear the input
        addTodoText.value = "";
        view.displayTodos();
    },
    //Change todo
    changeTodo: function(){
        var changeTodoText = document.getElementById("changeTodoText");
        var changeTodoPosition = document.getElementById("changeTodoPosition");

        //Get the CHANGE TODO method in the object TODOLIST and set these parameters
        todoList.changeTodo(changeTodoPosition.valueAsNumber, changeTodoText.value);
        //Clear the inputs
        changeTodoText.value = "";
        changeTodoPosition.value = "";
        //Call upon VIEW object and trigger displayTodos() method/function;
        view.displayTodos();
    },
    deleteTodo: function(position){
        todoList.deleteTodo(position);
        view.displayTodos();
    },
    deleteAll:function(position){
        todoList.todos.splice(position);
        view.displayTodos();
    },
    toggleCompleted: function(){
        var toggleCompletedInput = document.getElementById("toggleBtn");
        todoList.toggleCompleted(toggleCompletedInput.valueAsNumber);
        toggleCompletedInput.value = "";
        view.displayTodos();
    },
    toggleAll: function(){
            todoList.toggleAll();
            view.displayTodos();
    }
};
//Object that is used only to DISPLAY the items:
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");
            var todo = todoList.todos[i];
            var todoTextForCompleted = "";
            if (todo.completed === true){
                todoTextForCompleted = todo.todoText;
                todoLi.classList.toggle("checked");
            } else {
                todoTextForCompleted = todo.todoText;
                todoLi.classList.toggle("notCompleted");
            }
            todoLi.id = i;
            todoLi.textContent = todoTextForCompleted;
            todoLi.appendChild(this.createDeleteButton());
            todoLi.appendChild(this.createToggleButton());
            todosUl.appendChild(todoLi);
        }
    },

    createDeleteButton: function(){
        var deleteButton = document.createElement("button");
        deleteButton.textContent = "DELETE";
        deleteButton.className = "deleteBtn";
        return deleteButton;
    },
    createToggleButton: function(){
        var toggleButton = document.createElement("button");
        toggleButton.textContent = "Completed";
        toggleButton.id= "toggleBtn";
        return toggleButton;
    },
    //
    setUpEventListeners: function(){
        var todosUl = document.querySelector("ul");
        todosUl.addEventListener("click", function(event){
            var elementClicked = event.target;
            if(elementClicked.className === "deleteBtn"){
                handlers.deleteTodo(parseInt(elementClicked.parentNode.id));
            }

            if (elementClicked.id === "toggleBtn"){
                todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
                var toggleCompletedInput = document.getElementById("toggleBtn");                
                toggleCompletedInput.value = "";
                view.displayTodos();
                view.removeTodoButton();
            }
    });
  }
 }

view.setUpEventListeners();
5
  • Please provide all code to be able to test it, as code snippet would be great. Commented Apr 16, 2018 at 20:58
  • Having more than one element with a particular ID is invalid HTML. Use classes instead. Commented Apr 16, 2018 at 21:01
  • 1
    You mean something like a link to this whole todo list? If so, here it is on Glitch: glitch.com/edit/#!/skitter-aftermath?path=index.html:10:6 Should've provided it in the first place =) Commented Apr 16, 2018 at 21:02
  • Include the HTML too, pls. Commented Apr 16, 2018 at 21:09
  • I've uploaded all the code (HTML, CSS and JS) on the glitch link just above. Commented Apr 16, 2018 at 21:11

1 Answer 1

2

The code was a bit messy... the main problem I saw is that you were using multiple elements with same id (toggleBtn). I changed it to use a className. Here you have a working version following the design pattern (view-model-handlers).

The changes are:

toggleCompleted: function(position) {
  var toggleCompletedInput = document.querySelector("#" + position + " toggleBtn");

  var todoLi = document.createElement("li");
  var todo = todoList.todos[i];
  todoLi.id = i;
  todoLi.textContent = todo.todoText;
  if (todo.completed === true) {
    todoLi.classList.toggle("checked");
  } else {
    todoLi.classList.toggle("notCompleted");
    todoLi.appendChild(this.createToggleButton());
  }
  todoLi.appendChild(this.createDeleteButton());
  todosUl.appendChild(todoLi);

toggleButton.className = "toggleBtn";

  if (elementClicked.className === "toggleBtn") {
    todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
    view.displayTodos();
  }

And in CSS:

.toggleBtn {
  background-color: #eee;
  color: #e84118;
  cursor: pointer;
  position: absolute;
  right: 70px;
  bottom: 3px;
  animation: delete 0.5s ease-out 1 forwards;
}

.toggleBtn:hover {
  background-color: #4cd137;
  color: #eee;
}

.toggleBtn.removed {
  display: none;
}

//The object that holds the todo list:
var todoList = {
  todos: [],
  //Method for ADDING a TODO items
  addTodo: function(todoText) {
    this.todos.push({
      todoText: todoText,
      completed: false,
    });
  },
  //Method for changing the made TODO items
  changeTodo: function(position, newValue) {
    this.todos[position].todoText = newValue;
  },
  //Method for deleting TODO items:
  deleteTodo: function(position) {
    this.todos.splice(position, 1);
  },
  //Method for toggling a todo item as COMPLETED:
  toggleCompleted: function(position) {
    var todo = this.todos[position];
    todo.completed = !todo.completed;
  },
  //Method for toggling ALL todo items:
  toggleAll: function() {
    var totalTodos = this.todos.length;
    //Counting the ones that are completed
    var completedTodos = 0;

    //Getting the number of the completed todos:
    for (var i = 0; i < totalTodos; i++) {
      if (this.todos[i].completed === true) {
        //For every todo item that is completed, increase completedTodos by 1;
        completedTodos++;
      }
    }

    //Case 1: If everything is true, the turn it into FALSE;
    if (completedTodos === totalTodos) {
      for (var i = 0; i < totalTodos; i++) {
        this.todos[i].completed = false
      }
      //Case 2: Otherwise, make everything TRUE;
    } else {
      for (var i = 0; i < totalTodos; i++) {
        this.todos[i].completed = true;
      };
    };
  }
}

//Object for the BUTTON HANDLERS:
var handlers = {
  addTodo: function() {
    var addTodoText = document.getElementById("addTodoTextInput");

    todoList.addTodo(addTodoTextInput.value);
    //Clear the input
    addTodoText.value = "";
    view.displayTodos();
  },
  //Change todo
  changeTodo: function() {
    var changeTodoText = document.getElementById("changeTodoText");
    var changeTodoPosition = document.getElementById("changeTodoPosition");

    //Get the CHANGE TODO method in the object TODOLIST and set these parameters
    todoList.changeTodo(changeTodoPosition.valueAsNumber, changeTodoText.value);
    //Clear the inputs
    changeTodoText.value = "";
    changeTodoPosition.value = "";
    //Call upon VIEW object and trigger displayTodos() method/function;
    view.displayTodos();
  },
  deleteTodo: function(position) {
    todoList.deleteTodo(position);
    view.displayTodos();
  },
  deleteAll: function(position) {
    todoList.todos.splice(position);
    view.displayTodos();
  },
  toggleCompleted: function(position) {
    var toggleCompletedInput = document.querySelector("#" + position + " toggleBtn");
    todoList.toggleCompleted(toggleCompletedInput.valueAsNumber);
    toggleCompletedInput.value = "";
    view.displayTodos();
  },
  toggleAll: function() {
    todoList.toggleAll();
    view.displayTodos();
  }
};
//Object that is used only to DISPLAY the items:
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");
      var todo = todoList.todos[i];
      todoLi.id = i;
      todoLi.textContent = todo.todoText;
      if (todo.completed === true) {
        todoLi.classList.toggle("checked");
      } else {
        todoLi.classList.toggle("notCompleted");
        todoLi.appendChild(this.createToggleButton());
      }
      todoLi.appendChild(this.createDeleteButton());
      todosUl.appendChild(todoLi);
    }
  },

  createDeleteButton: function() {
    var deleteButton = document.createElement("button");
    deleteButton.textContent = "DELETE";
    deleteButton.className = "deleteBtn";
    return deleteButton;
  },
  createToggleButton: function() {
    var toggleButton = document.createElement("button");
    toggleButton.textContent = "Completed";
    toggleButton.className = "toggleBtn";
    return toggleButton;
  },
  //
  setUpEventListeners: function() {
    var todosUl = document.querySelector("ul");
    todosUl.addEventListener("click", function(event) {
      var elementClicked = event.target;
      if (elementClicked.className === "deleteBtn") {
        handlers.deleteTodo(parseInt(elementClicked.parentNode.id));
      }

      if (elementClicked.className === "toggleBtn") {
        todoList.toggleCompleted(parseInt(elementClicked.parentNode.id));
        view.displayTodos();
      }
    });
  }
}

view.setUpEventListeners();
* {
  margin: 0;
  padding: 0;
}

body {
  background-color: #dcdde1;
  font-family: 'Poppins', sans-serif;
}

li {
  color: #eee;
  margin: 5px 0;
  list-style-type: none;
  padding: 10px;
  background-color: grey;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  user-select: none;
  width: 100%;
  animation: listitem 1s 1 forwards;
  position: relative;
  border-radius: 5px;
}

li.deleted {
  animation: listItem 1s 1;
}

@keyframes listItem {
  from {
    opacity: 0
  }
  to {
    opacity: 1
  }
  ;
}

li.checked {
  background-color: #4cd137;
  color: #eee;
}

li.checked:before {
  color: #eee;
  margin: 0 10px;
  content: "\2714";
  animation: checkmark 1s 1 forwards;
}

@keyframes checkmark {
  0% {
    transform: scale(0);
  }
  60% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}

li.notCompleted {
  background-color: #e84118;
  color: #eee;
}

li.notCompleted:before {
  content: "\2716";
  animation: check 1s 1 forwards;
  margin: 0 10px;
}

@keyframes check {
  0% {
    transform: scale(0);
  }
  60% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}

ul li .deleteBtn {
  background-color: #eee;
  color: #e84118;
  cursor: pointer;
  position: absolute;
  right: 10px;
  bottom: 3px;
  animation: delete 0.5s ease-out 1 forwards;
}

.deleteBtn:hover {
  background-color: #353b48;
  color: #eee;
}

.toggleBtn {
  background-color: #eee;
  color: #e84118;
  cursor: pointer;
  position: absolute;
  right: 70px;
  bottom: 3px;
  animation: delete 0.5s ease-out 1 forwards;
}

.toggleBtn:hover {
  background-color: #4cd137;
  color: #eee;
}

.toggleBtn.removed {
  display: none;
}

@keyframes delete {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.05);
  }
  80% {
    transform: scale(0.95);
  }
  100% {
    transform: scale(1);
  }
}

.wrapper {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  padding: 10px;
}

h1 {
  color: #e84118;
  font-size: 35px;
  font-weight: 700;
  text-transform: uppercase;
}

h1 span {
  color: #0097e6;
  font-size: 65px;
  font-weight: 800;
  opacity: 0;
  letter-spacing: -50px;
  animation: headerSpan 2s 0.5s 1 forwards;
}

@keyframes headerSpan {
  0% {
    opacity: 0;
    letter-spacing: -50px;
    transform: scale(0);
  }
  50% {
    letter-spacing: 10px;
  }
  100% {
    opacity: 1;
    transform: scale(1);
    letter-spacing: 0px;
  }
}

input {
  padding: 5px;
  border: none;
  background-color: #f5f6fa;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
  border-radius: 3px;
  color: #e84118;
  font-family: 'Poppins';
  width: 100%;
  margin: 2px 0;
}

input:focus {
  outline: none !important;
}

input::placeholder {
  color: #e84118;
  font-family: "Poppins";
  font-style: italic;
  font-size: 12px;
  padding-left: 5px;
}

button {
  margin: 5px 0;
  padding: 3px;
  border: none;
  border-radius: 3px;
  font-family: 'Poppins';
  text-transform: uppercase;
  color: #eee;
  background-color: #00a8ff;
  font-weight: bold;
  font-size: 14px;
  transition: 0.2s ease-out;
}

button:focus {
  outline: none!important;
}

button:hover {
  background-color: #e84118;
  color: #eee;
}

.container_todo {
  border-radius: 5px;
  padding: 35px;
  display: flex;
  flex-direction: column;
  background-color: #f4f4f4;
  box-shadow: 0 2px 25px rgba(0, 0, 0, 0.25);
  animation: container 1s 1 forwards;
}

.toggleAllButtons {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}


.all.active {
  background-color: red;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>The Todo List</title>
  <link rel="stylesheet" type="text/css" href="styles.css">
  <link href="https://fonts.googleapis.com/css?family=Gaegu|Indie+Flower|Pacifico|Raleway" rel="stylesheet">
</head>

<body>


  <div class="wrapper">
    <!-- Title -->
    <h1>The <span>Todo</span> List !</h1>

    <!-- TODO LIST container -->
    <div class="container_todo">
      <!-- BUTTONS -->
      <!-- Add Todo item -->
      <div class="add">
        <button onclick="handlers.addTodo()">Add todo</button>
        <input type="text" id="addTodoTextInput" placeholder="Add a Todo item">
      </div>
      <!-- Change Todo Item-->
      <div class="change">
        <button onclick="handlers.changeTodo()">Change Todo</button>
        <input type="text" id="changeTodoText" placeholder="Change a Todo item">
        <input type="number" id="changeTodoPosition" placeholder="Choose which Item to change">
      </div>
      <!-- Toggle completed Todo item -->
      <div class="toggleAllButtons">
        <!-- Toggle All items button -->
        <div class="all">
          <button onclick="handlers.toggleAll()">Toggle All</button>
          <button onclick="handlers.deleteAll()">Delete All</button>
        </div>
      </div>

      <ul></ul>
    </div>
  </div>
  <!-- /////////////////////////////////////// -->
  <!-- JS link -->
  <script src="script.js"></script>
</body>

</html>

Sign up to request clarification or add additional context in comments.

4 Comments

First of all I must say HUGE Thanks for looking into and solving my problem, I really really appreciate it ! The code was indeed not a bit, but quite a bit messy, I admit that, it was mostly due to not paying attention to html/css so much rather than that I tried to make it work :D It works at its supposed to now, thanks again. Mind if I ask what exactly you changed in the JS part? Only the use of class names for the buttons and the append child in the "else" statement of the displayTodos function? Once again, thanks a lot!
I added the list of changes. Please, mark the answer as solved if it helped you :).
Got it, thanks! Marked the answer :D Sorry I didnt do that at first, I am new to stackoverflow so I still learn how things work around here. Great answer provided.
You're welcome. I see you put effort in the code, it feels bad when you're so close. Keep learning!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.