Ошибка неперехваченного типа: Не удается прочитать свойства неопределенного (чтение «isDone»)

#javascript

Вопрос:

Функциональность работает просто отлично, однако я получаю эту ошибку в консоли при нажатии на значок корзины при удалении элемента todo.

     Uncaught TypeError: Cannot read properties of undefined (reading 'isDone')
        at TodoList.done_undone (script.js:68)
        at HTMLLIElement.<anonymous> (script.js:104)
    done_undone @ script.js:68
    (anonymous) @ script.js:104
 

Вот ссылка на живую страницу: https://sarahschlueterportfolio.z22.web.core.windows.net/todolist.html

Вот мой код:

HTML

     <!-- Page Content -->
    <div class="container">

        <div class="todoHeader">
            <h1>My To Do List</h1>
            <input type="text" id="userInput" placeholder="Things to be done..." />
            <span class="addButton" id="add_button">Add</span>
        </div>
        <ul id="todoListItems">
        </ul>

    </div>
    <!-- /.container -->
 

JS

 
    const todoObjectList = [];
    
    class TodoList {
        constructor(item){
            this.ulElement = item;
        }
    
        add() {
            const todoInput = document.querySelector("#userInput").value;
    
            if (todoInput == "") {
                alert("Please enter an item.")
            } else {
                const todoObject = {
                    id : todoObjectList.length,
                    todoText : todoInput,
                    isDone : false,
                }
            
            todoObjectList.unshift(todoObject); 
            this.display();
            document.querySelector("#userInput").value = ''; 
            }
        }
    
        done_undone(x) {
            const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
            todoObjectList[selectedTodoIndex].isDone == false ? todoObjectList[selectedTodoIndex].isDone = true : todoObjectList[selectedTodoIndex].isDone = false;
            console.log(todoObjectList[selectedTodoIndex].isDone);
            this.display();
        }
    
    
        deleteElement(z) {
            const selectedDelIndex = todoObjectList.findIndex((item) => item.id == z);
            todoObjectList.splice(selectedDelIndex,1);
            this.display();
        }
    
        display() {
            this.ulElement.innerHTML = "";
            
            todoObjectList.forEach((objectItem) => {
                
                const liElement = document.createElement("li");
                const delButton = document.createElement("i");
                
                liElement.innerText = objectItem.todoText;
                liElement.setAttribute("data-id", objectItem.id);
                
                delButton.setAttribute("data-id", objectItem.id);
                delButton.classList.add("far", "fa-trash-alt"); 
    
                liElement.appendChild(delButton);
    
                delButton.addEventListener("click", function(e) {
                    const deleteId = e.target.getAttribute("data-id");
                    personalTodoList.deleteElement(deleteId);
                })
                liElement.addEventListener("click", function(e) {
                    const selectedId = e.target.getAttribute("data-id");
                    personalTodoList.done_undone(selectedId);
                })
                if (objectItem.isDone) {
                    liElement.classList.add("checked");
                }
                this.ulElement.appendChild(liElement);
            })
        }
    }
    
    const listSection = document.querySelector("#todoListItems");
    
    personalTodoList = new TodoList(listSection);
    
    document.querySelector(".addButton").addEventListener("click", function() {
        personalTodoList.add()
    })

 

Я пробовал искать разные решения. Один из них рекомендовал поместить скрипт в нижней части моего html-кода вместо верхней, что не имело никакого значения.

Я совершенно уверен, что что-то не так со строкой кода перед входом в консоль в методе done_undone.

Мы будем очень признательны за любое понимание того, как устранить эту ошибку!

Ответ №1:

при нажатии delete вы удаляете элемент по идентификатору

  deleteElement(z) {
     const selectedDelIndex = todoObjectList.findIndex((item) => item.id == z);
     todoObjectList.splice(selectedDelIndex,1); // element is removed from the array
     this.display();
 }
 

но done_undone вызывается внутри this.diaplay() в следующей строке

   done_undone(x) {
    const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
    // selectedTodoIndex here is -1 because element is already removed
 
    // add this condition to check if element is present in the array
    if (selectedTodoIndex !== -1) { 
      todoObjectList[selectedTodoIndex].isDone == false ? 
      todoObjectList[selectedTodoIndex].isDone = true : 
      todoObjectList[selectedTodoIndex].isDone = false;
    }
  }
 

Ответ №2:

Попробуйте проверить:

 done_undone(x) {
  const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
  if (selectedTodoIndex > -1) {
  ...
 
 const todoObjectList = [];
class TodoList {
  constructor(item){
    this.ulElement = item;
  }

  add() {
    const todoInput = document.querySelector("#userInput").value;
    if (todoInput == "") {
        alert("Please enter an item.")
    } else {
        const todoObject = {
          id : todoObjectList.length,
          todoText : todoInput,
          isDone : false,
        }
      todoObjectList.unshift(todoObject); 
      this.display();
      document.querySelector("#userInput").value = ''; 
    }
  }

  done_undone(x) {
    const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
    if (selectedTodoIndex > -1) {
      odoObjectList[selectedTodoIndex].isDone == false ? 
       todoObjectList[selectedTodoIndex].isDone = true :
       todoObjectList[selectedTodoIndex].isDone = false;

      this.display();
    }
  }
    
  deleteElement(z) {
    const selectedDelIndex = todoObjectList.findIndex((item) => item.id == z);
    todoObjectList.splice(selectedDelIndex,1);
    this.display();
  }

  display() {
    this.ulElement.innerHTML = "";

    todoObjectList.forEach((objectItem) => {

      const liElement = document.createElement("li");
      const delButton = document.createElement("i");

      liElement.innerText = objectItem.todoText;
      liElement.setAttribute("data-id", objectItem.id);

      delButton.setAttribute("data-id", objectItem.id);
      delButton.classList.add("far", "fa-trash-alt"); 

      liElement.appendChild(delButton);

      delButton.addEventListener("click", function(e) {
        const deleteId = e.target.getAttribute("data-id");
        personalTodoList.deleteElement(deleteId);
      })
      liElement.addEventListener("click", function(e) {
        const selectedId = e.target.getAttribute("data-id");
        personalTodoList.done_undone(selectedId);
      })
      if (objectItem.isDone) {
        liElement.classList.add("checked");
      }
      this.ulElement.appendChild(liElement);
    })
  }
}
    
const listSection = document.querySelector("#todoListItems");

personalTodoList = new TodoList(listSection);

document.querySelector(".addButton").addEventListener("click", function() {
  personalTodoList.add()
}) 
 <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg p" crossorigin="anonymous"/>
<!-- Page Content -->
<div class="container">
  <div class="todoHeader">
    <h1>My To Do List</h1>
    <input type="text" id="userInput" placeholder="Things to be done..." />
    <span class="addButton" id="add_button">Add</span>
  </div>
  <ul id="todoListItems">
  </ul>
</div>
<!-- /.container -->