Basically, I am trying to add the functionality where, upon clicking the blue button, the reading status would change to either <Yes> or <Not>. Any leads on that sense would be much appreciated. Secondary, I am lost on why at them moment, I am getting the alert when that button is clicked, The book is not being deleted (unless you click on the correct icon) but still... I can't figure our the flaw on my logic!!
JS Code:
// Book Class: Represents a Book
class Book {
constructor(title, author, pages, isRead) {
this.title = title;
this.author = author;
this.pages = pages;
this.isRead = isRead;
}
}
// UI Class: Handle UI Tasks
class UI {
static displayBooks() {
const books = Store.getBooks();
books.forEach((book) => UI.addBookToList(book));
}
static addBookToList(book) {
const list = document.querySelector("#book-list");
const row = document.createElement("tr");
row.innerHTML = `
<td>${book.title}</td> </button>
<td>${book.author}</td>
<td>${book.pages}</td>
<td><button >${book.isRead}</button></td>
<td><a href="#" >X</a></td>
`;
list.appendChild(row);
}
static deleteBook(el) {
if (el.classList.contains("delete")) {
el.parentElement.parentElement.remove();
}
}
static showAlert(message, className) {
const div = document.createElement("div");
div.className = `alert alert-${className}`;
div.appendChild(document.createTextNode(message));
const container = document.querySelector(".container");
const form = document.querySelector("#book-form");
container.insertBefore(div, form);
// Vanish in 3 seconds
setTimeout(() => document.querySelector(".alert").remove(), 3000);
}
static clearFields() {
document.querySelector("#title").value = "";
document.querySelector("#author").value = "";
document.querySelector("#pages").value = "";
document.querySelector("#isRead").value = "";
}
}
// Store Class: Handles Storage
class Store {
static getBooks() {
let books;
if (localStorage.getItem("books") === null) {
books = [];
} else {
books = JSON.parse(localStorage.getItem("books"));
}
return books;
}
static addBook(book) {
const books = Store.getBooks();
books.push(book);
localStorage.setItem("books", JSON.stringify(books));
}
static removeBook(pages) {
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.pages === pages) {
books.splice(index, 1);
}
});
localStorage.setItem("books", JSON.stringify(books));
}
}
// Event: Display Books
document.addEventListener("DOMContentLoaded", UI.displayBooks);
// Event: Add a Book
document.querySelector("#book-form").addEventListener("submit", (e) => {
// Prevent actual submit
e.preventDefault();
// Get form values
const title = document.querySelector("#title").value;
const author = document.querySelector("#author").value;
const pages = document.querySelector("#pages").value;
const isRead = document.querySelector("#isRead").value;
// Validate
if (title === "" || author === "" || pages === "" || isRead === "") {
UI.showAlert("Please fill in all fields", "danger");
} else {
// Instatiate book
const book = new Book(title, author, pages, isRead);
// Add Book to UI
UI.addBookToList(book);
// Add book to store
Store.addBook(book);
// Show success message
UI.showAlert("Book Added", "success");
// Clear fields
UI.clearFields();
}
});
// Event: Remove a Book
document.querySelector("#book-list").addEventListener("click", (e) => {
// Remove book from UI
UI.deleteBook(e.target);
// Remove book from store
Store.removeBook(
e.target.parentElement.previousElementSibling.previousElementSibling
.textContent
);
// Show success message
UI.showAlert("Book Removed", "success");
});
HTML Code:
!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" />
<title>My BookListApp</title>
<link
rel="stylesheet"
href="https://bootswatch.com/4/yeti/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm"
crossorigin="anonymous"
/>
</head>
<body>
<div >
<h1 >
<i ></i>My
<span >BookList</span> App
</h1>
<form id="book-form">
<div >
<label for="title">Title:</label>
<input type="text" id="title" maxlength="30" />
</div>
<div >
<label for="author">Author:</label>
<input type="text" id="author" maxlength="20" />
</div>
<div >
<label for="pages">Pages:</label>
<input
type="number"
id="pages"
min="1"
max="10000"
/>
</div>
<div >
<label for="isRead">Read:</label>
<select type="number" id="isRead" >
<option value="" selected disabled hidden></option>
<option value="Yes">Yes</option>
<option value="No">No</option>
</select>
</div>
<input
type="submit"
value="Add Book"
/>
</form>
<table >
<thead>
<tr>
<th>Title:</th>
<th>Author:</th>
<th>Pages:</th>
<th>Read:</th>
<th></th>
</tr>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script src="./src/app.js"></script>
</body>
</html>
Thanks!
CodePudding user response:
This onclick is too broad:
document.querySelector("#book-list").addEventListener("click",
It's triggered for every click anywhere in the books list/table. Not just on buttons. Anywhere. If you don't click on a button, JS breaks. If you hit the Yes/No button, JS doesn't break, but it tries to delete the book, kinda.
Broken jsfiddle: https://jsfiddle.net/do42Lkqn/
Solution: explicitly check for which button is clicked. Inside the click handler, you could do:
if (!e.target.closest('.delete')) return;
So if you didn't click a .delete element, it won't do anything.
