Odin Project图书馆应用切换阅读状态时出现Cannot read properties of undefined错误
问题描述
基于The Odin Project开发图书馆应用时,点击「切换阅读状态」按钮尝试将书籍状态从「未读」改为「已读」时,出现Cannot read properties of undefined错误,错误发生在读取myLibrary[parseInt(retrieveBookToToggle)].status的代码行。
错误代码
console.log( "What is the toggle initial value?...", myLibrary[parseInt(retrieveBookToToggle)].status ); let myLibrary = []; const btnAdd = document.querySelector("btn-add"); //Object Constructor class Book { constructor(title, author, pages, status) { this.title = title; this.author = author; this.pages = pages; this.status = status; } //Add a new book to array function addBookToLibrary(title, author, pages, status) { let book = new Book(title, author, pages, status); myLibrary.push(book); displayBooksOnPage() } //Display array to card function displayBooksOnPage() { const books = document.querySelector(".books-container"); //remove displayed cards before loop array const removeDivs = document.querySelectorAll(".card"); console.log("Show me the node count of the current card divs........", removeDivs); for (let i = 0; i < removeDivs.length; i++) { removeDivs[i].remove(); } //Loop Library array and display to the cards let index = 0; myLibrary.forEach(myLibrarys => { const card = document.createElement("div"); card.classList.add("card"); books.appendChild(card); //remove book button const removeBookButton = document.createElement("button"); removeBookButton.classList.add("remove-book-button"); removeBookButton.textContent = "Remove Book"; console.log("show me my currentnarray objects inside of foreach........", myLibrary); //link data attribute of the remove button to the array and card removeBookButton.dataset.linkedArray = index; index++; console.log("show me dataset link back to the array...", removeBookButton.dataset.linkedArray); card.appendChild(removeBookButton); //start event listener/remove array item from array and card from parent div via data link removeBookButton.addEventListener("click", removeBookFromLibrary); function removeBookFromLibrary() { let retrieveBookToRemove = removeBookButton.dataset.linkedArray; console.log("attempting to remove array item via data attribute...", parseInt(retrieveBookToRemove)); myLibrary.splice(parseInt(retrieveBookToRemove), 1); card.remove(); displayBooksOnPage; } const readStatusButton = document.createElement("button"); readStatusButton.classList.add("read-status-button"); readStatusButton.textContent = "Change Status"; readStatusButton.dataset.linkedArray = index; console.log("show dataset link back to the array for read status button", readStatusButton.dataset.linkedArray); card.appendChild(readStatusButton); readStatusButton.addEventListener("click", toggleReadStatus); function toggleReadStatus() { let retrieveBookToToggle = readStatusButton.dataset.linkedArray; Book.prototype = Object.create(Book.prototype); const toggleBook = new Book(); console.log("what's the toggle initial value? ", myLibrary[parseInt(retrieveBookToToggle)].status); if (myLibrary[parseInt(retrieveBookToToggle)].status == "Read") { toggleBook.status = "Not Read"; myLibrary[parseInt(retrieveBookToToggle)].status = toggleBook.status; } else if (myLibrary[parseInt(retrieveBookToToggle)].status == "Not Read") { toggleBook.status = "Read"; myLibrary[parseInt(retrieveBookToToggle)].status = toggleBook.status; } displayBooksOnPage(); } for (let key in myLibrarys) { const para = document.createElement("p"); para.textContent = (`${myLibrarys[key]}`); card.appendChild(para); } }) } const addBookButton = document.querySelector(".btn-add"); addBookButton.addEventListener("click", displayTheForm); function displayTheForm() { document.getElementById("books-form").style.display = ""; } const submitButton = document.querySelector(".btn-add"); submitButton.addEventListener("click", intakeFormData); function intakeFormData() { let Title = document.getElementById("title").value; let Author = document.getElementById("author").value; let Pages = document.getElementById("pages").value; let Status = document.getElementById("status").value; if ((Title == "") || (Author == "") || (Pages == "") || (Status == "")) { return; } addBookToLibrary(Title, Author, Pages, Status); document.getElementById("add-book").reset(); } const clearButton = document.getElementById("btn-reset"); clearButton.addEventListener("click", clearForm()); function clearForm() { document.getElementById("add-book").reset(); }
修复方案
1. 修正状态按钮的索引值(核心错误)
错误原因:给「切换状态」按钮的dataset.linkedArray赋值时,用的是index++之后的数值,导致索引比实际数组元素的索引大1,访问时超出数组范围拿到undefined。
修复代码:
// 移除按钮的索引赋值保持不变 removeBookButton.dataset.linkedArray = index; // 状态按钮同样使用当前的index,不要用自增后的数值 readStatusButton.dataset.linkedArray = index; // 统一在两个按钮赋值后再执行index自增 index++;
调整后的关键代码片段:
// 给移除按钮赋值索引 removeBookButton.dataset.linkedArray = index; // 给状态按钮赋值相同的当前索引 readStatusButton.dataset.linkedArray = index; // 最后执行索引自增 index++;
2. 修正类内部的函数声明错误
class内部不能用function关键字声明方法,需要把addBookToLibrary、displayBooksOnPage等函数移到类外部,因为这些是全局操作逻辑,不属于Book实例的方法。
示例:
let myLibrary = []; const btnAdd = document.querySelector("btn-add"); //Object Constructor class Book { constructor(title, author, pages, status) { this.title = title; this.author = author; this.pages = pages; this.status = status; } } // 将addBookToLibrary移到类外部 function addBookToLibrary(title, author, pages, status) { let book = new Book(title, author, pages, status); myLibrary.push(book); displayBooksOnPage() } // displayBooksOnPage同样移到类外部 function displayBooksOnPage() { // ...原函数内容 }
3. 移除toggleReadStatus中多余的Book实例创建
切换状态时不需要重新创建Book对象,直接修改数组中对应元素的status即可:
function toggleReadStatus() { let retrieveBookToToggle = readStatusButton.dataset.linkedArray; const book = myLibrary[parseInt(retrieveBookToToggle)]; if (book.status === "Read") { book.status = "Not Read"; } else { book.status = "Read"; } displayBooksOnPage(); }
4. 修正事件监听的函数引用错误
removeBookFromLibrary中调用displayBooksOnPage时缺少括号,应该是displayBooksOnPage();clearButton的事件监听错误地执行了clearForm(),应该传递函数引用clearForm:
clearButton.addEventListener("click", clearForm);
修复后的完整代码
let myLibrary = []; const btnAdd = document.querySelector("btn-add"); //Object Constructor class Book { constructor(title, author, pages, status) { this.title = title; this.author = author; this.pages = pages; this.status = status; } } //Add a new book to array function addBookToLibrary(title, author, pages, status) { let book = new Book(title, author, pages, status); myLibrary.push(book); displayBooksOnPage() } //Display array to card function displayBooksOnPage() { const books = document.querySelector(".books-container"); //remove displayed cards before loop array const removeDivs = document.querySelectorAll(".card"); console.log("Show me the node count of the current card divs........", removeDivs); for (let i = 0; i < removeDivs.length; i++) { removeDivs[i].remove(); } //Loop Library array and display to the cards let index = 0; myLibrary.forEach(myLibrarys => { const card = document.createElement("div"); card.classList.add("card"); books.appendChild(card); //remove book button const removeBookButton = document.createElement("button"); removeBookButton.classList.add("remove-book-button"); removeBookButton.textContent = "Remove Book"; console.log("show me my current array objects inside of foreach........", myLibrary); //read status button const readStatusButton = document.createElement("button"); readStatusButton.classList.add("read-status-button"); readStatusButton.textContent = "Change Status"; //link data attribute for both buttons removeBookButton.dataset.linkedArray = index; readStatusButton.dataset.linkedArray = index; index++; console.log("show me dataset link back to the array...", removeBookButton.dataset.linkedArray); console.log("show dataset link back to the array for read status button", readStatusButton.dataset.linkedArray); card.appendChild(removeBookButton); card.appendChild(readStatusButton); //remove book event listener removeBookButton.addEventListener("click", removeBookFromLibrary); function removeBookFromLibrary() { let retrieveBookToRemove = removeBookButton.dataset.linkedArray; console.log("attempting to remove array item via data attribute...", parseInt(retrieveBookToRemove)); myLibrary.splice(parseInt(retrieveBookToRemove), 1); card.remove(); displayBooksOnPage(); } //toggle status event listener readStatusButton.addEventListener("click", toggleReadStatus); function toggleReadStatus() { let retrieveBookToToggle = readStatusButton.dataset.linkedArray; const book = myLibrary[parseInt(retrieveBookToToggle)]; console.log("what's the toggle initial value? ", book.status); book.status = book.status === "Read" ? "Not Read" : "Read"; displayBooksOnPage(); } //render book info for (let key in myLibrarys) { const para = document.createElement("p"); para.textContent = (`${myLibrarys[key]}`); card.appendChild(para); } }) } const addBookButton = document.querySelector(".btn-add"); addBookButton.addEventListener("click", displayTheForm); function displayTheForm() { document.getElementById("books-form").style.display = ""; } const submitButton = document.querySelector(".btn-add"); submitButton.addEventListener("click", intakeFormData); function intakeFormData() { let Title = document.getElementById("title").value; let Author = document.getElementById("author").value; let Pages = document.getElementById("pages").value; let Status = document.getElementById("status").value; if ((Title === "") || (Author === "") || (Pages === "") || (Status === "")) { return; } addBookToLibrary(Title, Author, Pages, Status); document.getElementById("add-book").reset(); } const clearButton = document.getElementById("btn-reset"); clearButton.addEventListener("click", clearForm); function clearForm() { document.getElementById("add-book").reset(); }
内容的提问来源于stack exchange,提问作者Eder Pagliotto
相关产品推荐
相关产品推荐

