I created a constructor function name Book and an empty array named myLibrary as a container for these new book object instances. A separate function to create a book createBook calls a function addBookToLibrary which pushes the book into the myLibrary array, clears the form to add books, then renders the book list via the viewBookList function.
Each time I call createBook and create a new instance of Book, I'm running into a problem where the rendered list of books that show up on the page repeat all instances of Book objects created prior to the new book being added plus the one new book. For instance, I have book1, book2 and book3 that I previously created. I then add book4 so now the array has [ {book1...}, {book2...}, {book3...}, {book4...} ] objects correctly in place, but what's being rendered on the page is a scattered mess of book1, book1, book2, book1, book2, book3, book1, book2, book3, book4 as you see in the attached image. You'll see the array in the console is fine.
I initially cleared out the myLibrary array in createBook which correctly rendered the books to the page, but obviously wipes out my array which won't work because I need to be able to delete these at some point from the array. So my question is, how do I display what's in my array onto the page each time without rendering duplicates of books that were already added?
const submitButton = document.querySelector('#submit');
const btnOpenModal = document.querySelector('.open-modal');
const btnCloseModal = document.querySelector('.close-modal');
const modalElement = document.querySelector('.modal');
// let myLibrary = [
// new Book('Down and Out in Paris and London', 'George Orwell', 232, true),
// new Book('Homage to Catalonia', 'George Orwell', 202, true),
// new Book('Shooting an Elephant', 'George Orwell', 368, false),
// ];
// let count = myLibrary.length - 3;
let myLibrary = [];
let count = myLibrary.length;
// Constructor...
function Book(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = Number(pages);
this.read = Boolean(read);
// Methods
this.bookInfo = function () {
const wasRead = this.read === true ? 'read' : 'not read';
return `${this.title} written by ${this.author}, ${this.pages} pages in length was ${wasRead}.`;
};
return this.bookInfo();
}
function createBook(e) {
e.preventDefault();
// myLibrary = [];
let title = document.querySelector('#title').value;
let author = document.querySelector('#author').value;
let pages = document.querySelector('#pages').value;
let read = document.querySelector('#read').value;
// Instantiate new Book object
const newBook = new Book(title, author, pages, read); // Add ID when instantiating
addBookToLibrary(newBook);
clearForm();
viewBookList(myLibrary);
}
function addBookToLibrary(book) {
// Hide modal
removeClass();
// Add book to array
return myLibrary.push(book);
}
function clearForm() {
document.querySelector('#form').reset();
}
function setCardStyle(element, details) {
element.setAttribute(
'style',
'display: flex; flex-direction: column; justify-content: space-between; text-align: center; background-color: #fff; padding: 1em; margin: 1em 1em 1em 0; border-radius: 5px; height: 250px; width: 250px; line-height: 1.5; box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);',
);
element.innerHTML = `
<h3 class="title">${details.title}</h3>
<br>
<hr>
<br>
<p>${details.author}</p>
<p>${details.pages} pages</p>
<p>${details.read === true ? 'Read' : 'Unread'}</p>`;
}
function createCard() {
let bookCard = document.createElement('div');
bookCard.classList.add('card');
bookCard.setAttribute('data-target', `${count++}`); // Set target ID
return bookCard;
}
function removeBookBtn() {
let btn = document.createElement('button');
// Style button
btn.setAttribute(
'style',
'color: red; height: 2.5em; width: 50%; border-radius: 5px; margin: 0 auto; font-weight: bold; text-transform: uppercase; cursor: pointer;',
);
btn.innerHTML = 'Delete';
return btn;
}
function handleDelete(e) {
// Get book's data-target
let bookIndex = parseInt(e.path[1].attributes[1].value);
// console.log(bookIndex);
myLibrary.splice(bookIndex, 1);
// console.log(newArray);
// myLibrary = myLibrary.filter((book, index) => index !== bookIndex);
return viewBookList(myLibrary);
}
function clearDOM(element) {
while (element.firstElementChild) {
element.firstElementChild.remove();
}
}
function viewBookList(list) {
const bookDiv = document.querySelector('.book-list');
clearDOM(bookDiv);
for (book in list) {
let bookDetails = list[book];
let renderCard = createCard();
const deleteButton = removeBookBtn();
deleteButton.addEventListener('click', handleDelete);
setCardStyle(renderCard, bookDetails);
renderCard.appendChild(deleteButton);
bookDiv.appendChild(renderCard);
}
return bookDiv;
}
function addClass() {
return modalElement.classList.add('open');
}
function removeClass() {
return modalElement.classList.remove('open');
}
// Event listeners
btnOpenModal.addEventListener('click', addClass);
btnCloseModal.addEventListener('click', removeClass);
submitButton.addEventListener('click', createBook);
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
:root {
--roboto: 'Roboto', sans-serif;
--pt: "PT Sans", sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: var(--roboto);
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
background-image: linear-gradient(to top, #a6c1ee 0%, #fbc2eb 100%);
}
.header {
text-align: center;
}
.header h1 {
color: #fff;
font-size: 4rem;
text-shadow: 1px 2px 5px #000;
}
.modal {
padding: 1em;
background-color: #fff;
}
.label {
text-transform: capitalize;
}
.btn {
font-family: "PT Sans", sans-serif;
font-size: 1.2rem;
padding: 1rem 2.5rem;
cursor: pointer;
border: none;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
}
.open-modal {
background: #fff;
border-radius: 5px;
box-shadow: 0 5px 10px 2px rgba(0, 0, 0, 0.2);
transition: all 0.2s linear;
}
.open-modal:hover {
box-shadow: none;
background: rgba(255, 255, 255, 0.8);
}
.modal.open {
display: flex;
}
.form {
padding: 5em;
}
.modal-container {
background-color: #fff;
width: 90%;
max-width: 450px;
position: relative;
}
.modal.open .modal-container {
animation: move 0.6s ease-out;
}
@keyframes move {
from {
transform: translateY(-50px);
}
to {
transform: translateY(0px);
}
}
.close-modal {
font-size: 3rem;
position: absolute;
top: 0;
right: 0;
background-color: #fff;
height: 40px;
width: 40px;
text-align: center;
line-height: 40px;
cursor: pointer;
transition: color 0.15s linear;
}
.close-modal:hover {
color: #f00;
}
.book-list {
display: flex;
flex-wrap: wrap;
margin-top: 1.5em;
gap: 2em;
}
/* Created in app.js */
/* .card {
} */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles/style.css">
<script src="scripts/app.js" defer></script>
<title>Odin Library</title>
</head>
<body>
<div class="container">
<header class="header">
<h1>Library</h1>
</header>
<section class="modal open">
<div class="modal-container">
<span id="close-modal" class="close-modal">×</span>
<form action="" method="POST" name="bookForm" id="form" class="form">
<div class="form-row">
<label for="title" class="label">title: </label>
<input type="text" name="title" id="title" placeholder="Animal Farm" required>
</div>
<div class="form-row">
<label for="author" class="label">author: </label>
<input type="text" name="author" id="author" placeholder="George Orwell" required>
</div>
<div class="form-row">
<label for="pages" class="label">pages: </label>
<input type="number" name="pages" id="pages" placeholder="232" required>
</div>
<div class="form-row">
<label for="read" class="label">read: </label>
<input type="checkbox" name="read" id="read" required>
</div>
<div class="button-container">
<input type="submit" value="Create" id="submit" class="submit">
</div>
</form>
</div>
</section class="card-section">
<section>
<!-- Add plus icon -->
<button class="btn open-modal" id="open-modal">Add Book</button>
<div class="book-list">
<!-- Container for cards -->
</div>
</section>
</div>
</body>
</html>

viewBookListshould first clear out the container of all children?viewBookListsimply always adds all items you give it to the page. The previous things on the page are not cleared.clearDOMto clear the DOM and called it inviewBookList. Updated the code snippet as well. Thanks.