I’m running into an issue with my current JS project. It’s a simple library where the user inputs the info and spits it out onto the page. I have a delete button that I add to each new div, which I’ve added event listeners to the buttons. When I click delete, it will delete the first one – but that’s it. It stops working if i want to delete more than one book.
I think my problem is how I’m targeting the data-set/index values that i assign each div, but I’m not sure. I’ve tried for loops, for each, etc and can’t seem to figure it out.
Any help would be appreciated.
const book1 = new Book('inserttitlehere', 'His name?', 63, false) const book2 = new Book('kill bill', 'author2', 653, false) const book3 = new Book('oh yeah baby', 'author3', 323, false) const book4 = new Book('kill me now', 'author4', 132, true) library.push(book1, book2, book3, book4) // Book constructor function Book(title, author, pages, completed) { this.title = title this.author = author this.pages = pages this.completed = completed } const main = document.querySelector('main'); const form = document.querySelector('.form'); //Function to add books to the DOM function displayBooks() { let dataIndex = 0; main.innerHTML = ''; library.forEach(book => { const bookElement = document.createElement('div') bookElement.classList.add('book'); bookElement.setAttribute('data-index', dataIndex); bookElement.innerHTML = ` <h3> ${book.title} </h3> <p> Author: ${book.author}</p> <p> Pages: ${book.pages}</p> Completed: <input type="checkbox"> ${book.completed} <br> <button class="delete">Delete</button> ` main.appendChild(bookElement); dataIndex++; }) } displayBooks(); //Add new book to library function addBookToLibrary(title, author, pages, completed) { const newBook = new Book(title, author, pages, completed) library.push(newBook); } //Deleting a book from the array let deleteBtns = document.querySelectorAll('.book .delete'); deleteBtns.forEach(button => { button.addEventListener('click', () => { const index = button.parentNode.dataset['data-index']; deleteBook(index); }) }); function deleteBook(index) { library.splice(index, 1); displayBooks(); } form.addEventListener('submit', (e) => { e.preventDefault(); const title = document.querySelector('#title').value; const author = document.querySelector('#author').value; const pages = document.querySelector('#pages').value; addBookToLibrary(title, author, pages, false); document.forms[0].reset(); }) console.log(library); console.log(deleteBtns);
!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css"> <title>Library</title> </head> <body> <nav> <h1>Library</h1> <button class="add-book-btn">NEW BOOK</button> </nav> <main></main> <div class="modal"> <form class="form"> <p class="close"></p> <h2>Add Book</h2> <div class="user-input"> <label for="">Title</label><br> <input type="text" id="title" required> </div> <div class="user-input"> <label for="">Author</label><br> <input type="text" id="author" required> </div> <div class="user-input"> <label for="">Pages</label><br> <input type="number" id="pages" required> </div> <div class="user-input"> <label for="">Read?</label> <input type="checkbox" id="read"> </div> <button type="submit" id="submit">Submit</button> </form> </div> <script src="script.js"></script> </body> </html>
Advertisement
Answer
When you are building an app like this it’s often best to remove the parts that aren’t relevant such as the form and all it;s associated functions in order to work on specific parts of it like these user interactions.
Here’s a scaled down version with a completely different approach that adds event listeners to the book elements individually as you create them.
Then instead of worrying about indexing, use array methods to find the book object in the library. So rather than rebuild all the elements when you remove one you simply remove both the element and the object in the array.
It’s broken down into smaller functions like addBookEvents()
then within each different event handler uses either Array.prototype.find()
or Array.prototype.findIndex()
to modify library.
Your approach of rebuilding all the elements just to change the indexing is not very scalable or efficient. In order to create the library array used here I just used your library.push(book1,book2...)
and then ran console.log(JSON.stringify(library))
and pasted it into this code to keep it lean for this stage.
Note that I changed data-index
to data-title
on the elements with assumption that titles will be unique in the array. That then allows searching array to find the specific book object. Using a unique book id
is more reliable in case of title duplications
const main = document.getElementById('books-list') library.forEach(createBookElement) function handleDeleteClick(event) { const bookElem = event.target.closest('.book'); const title = bookElem.dataset.title; bookElem.remove(); removeFromLibrary(title) console.log(title) } function handleCompleteChange(event){ const bookElem = event.target.closest('.book'); const title = bookElem.dataset.title; const checked = event.target.checked; toggleLibraryComplete(title, checked) } function removeFromLibrary(title) { const idx = library.findIndex(book => book.title === title); if (idx > -1) { library.splice(idx, 1); console.log('library length =', library.length) } } function toggleLibraryComplete(title, checked){ const book = library.find(book => book.title === title); book.completed = checked; console.log(JSON.stringify(book)) } function addBookEvents(bookElement){ const button = bookElement.querySelector('.delete'); button.addEventListener('click', handleDeleteClick); const checkbox = bookElement.querySelector('.book-complete'); checkbox.addEventListener('change', handleCompleteChange); } function createBookElement(book) { const bookElement = document.createElement('div') bookElement.classList.add('book'); bookElement.setAttribute('data-title', book.title); bookElement.innerHTML = ` <h3> ${book.title} </h3> <p> Author: ${book.author}</p> <p> Pages: ${book.pages}</p> Completed: <input class="book-complete" type="checkbox" ${book.completed && 'checked'}> ${book.completed} <br> <button class="delete">Delete</button> `; // add the event listeners for delete and completed addBookEvents(bookElement); main.appendChild(bookElement); } //console.log(JSON.stringify(library))
<main id="books-list"></main> <script> const library = [{"title":"inserttitlehere","author":"His name?","pages":63,"completed":true},{"title":"kill bill","author":"author2","pages":653,"completed":false},{"title":"oh yeah baby","author":"author3","pages":323,"completed":false},{"title":"kill me now","author":"author4","pages":132,"completed":true}]; </script>