I'm trying to use the value that each book has on its "status" and make the function "changeStatus()" run but "book.status" is defined inside a function, how can I get it? or is there any other way i can change the button behavior on click? I tried using a querySelector but it only allows me to click one button per refresh
let books = [];
const $name = document.querySelector("#name");
const $author = document.querySelector("#author");
const $status = document.querySelector("#status");
const $pages = document.querySelector("#pages");
const $tableBody = document.querySelector("#book-table-body");
function addBookToTable() {
// checkLocalStorage();
$tableBody.innerHTML = "";
books.forEach((book) => {
const htmlBook = `
<tr>
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.pages}</td>
<td><button onclick="changeStatus()">${book.status}</button></td>
<td><button >delete</button></td>
</tr>
`;
$tableBody.insertAdjacentHTML("afterbegin", htmlBook);
});
}
function changeStatus(){
}
const addBook = (event) => {
event.preventDefault();
let book = {
title: $("#title").val(),
author: $("#author").val(),
pages: $("#pages").val(),
status: $("#status").val(),
};
const cb = document.querySelector("#status");
if (cb.checked === true) {
book.status = "read";
} else {
book.status = "not read";
}
books.push(book);
document.forms[0].reset();
// Update DOM
addBookToTable(book);
const deleteBtn = document.querySelector(".delete");
deleteBtn.addEventListener("click", function deleteBook(){
books.splice(books.indexOf(book), 1);
addBookToTable(book);
})
// const myBtn = document.querySelector(".status-button");
// myBtn.addEventListener('click', function changeStatus(){
// if (book.status === "read"){
// myBtn.innerHTML="not read"
// book.status = "not read"
// }
// else if(book.status === "not read"){
// myBtn.innerHTML = "read";
// book.status = "read"
// }
// }
// );
localStorage.setItem("myMangaList", JSON.stringify(books));
};
function popForm() {
$("#popup").removeClass("hide");
}
function minimizeForm(){
$("#popup").addClass("hide");
}
function hideForm() {
$("#popup").addClass("hide");
$("#main-page").removeClass("hide");
}
function toggle() {
$("#main-page").addClass("hide");
}
* {
font-family: 'Lato', sans-serif;
text-align: center;
/* background-image: url(./images/image.jpg);
background-position: bottom;
background-size: cover; */
}
.hide {
display: none;
}
.hide-form {
background-color: transparent;
color: white;
border: none;
cursor: pointer;
font-size: 1.5rem;
margin-top: 20px;
margin-right: 15px;
position: absolute;
top: 0;
right: 0;
}
h1 {
font-size: 2.5rem;
font-weight: 900;
font-family: 'Work Sans', sans-serif;
}
h1 span {
color: #48abe0;
}
table {
border-collapse: collapse;
width: 100%;
}
td,
th {
/* border: 1px solid #dddddd; */
text-align: center;
padding: 8px;
}
#main-page {
margin-right: 80px;
margin-left: 80px;
}
#addBook {
border-radius: 70%;
background-color: #48abe0;
color: white;
border: none;
padding: 5px;
font-size: 31px;
height: 65px;
width: 65px;
box-shadow: 0 2px 4px darkslategray;
cursor: pointer;
transition: all 0.2s ease;
position: fixed;
bottom: 25px;
right: 25px;
}
#popup {
/* display: flex; */
justify-content: center;
align-items: center;
flex-direction: column;
gap: 30px;
padding-top: 50px;
padding-bottom: 50px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
background: #48abe0;
border-radius: 10px;
}
#popup input {
width: 80%;
padding: 15px;
margin-top: 25px;
border: 1px solid;
border-radius: 5px;
outline: none;
color: white;
font-weight: bold;
font-size: 1em;
background: #48abe0;
}
.status-box {
margin-top: 25px;
}
<!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">
<!-- Stylesheet -->
<link rel="stylesheet" href="style.css">
<!-- fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Edu NSW ACT Foundation:wght@500&family=Edu QLD Beginner:wght@400;600;700&family=Edu TAS Beginner:wght@700&family=Josefin Sans:wght@300&family=Lato&family=Montserrat:wght@100;600&family=Mouse Memoirs&family=Poppins:ital,wght@0,500;1,200&family=Quicksand:wght@300&family=Ubuntu:wght@300&family=Work Sans:wght@200&display=swap"
rel="stylesheet">
<title>Library</title>
</head>
<body>
<h1>My <span>Manga</span> Library</h1>
<div id="popup" >
<form>
<button onclick=" minimizeForm()">X</button>
<!-- <label for="title">Manga Title:</label> -->
<input type="text" id="title" placeholder="Title eg: One Piece"> <br>
<!-- <label for="author">Author:</label> -->
<input type="text" id="author" placeholder="Author eg: Eichiro Oda"><br>
<!-- <label for="pages">Pages:</label> -->
<input type="text" id="pages" placeholder="Pages eg: 2000"><br>
<div >
<label for="status">Read the book</label>
<input type="checkbox" id="status" name="status" value="">
</div>
<button type="submit" id="submit" onclick="addBook(event); hideForm()">Submit</button>
</form>
</div>
<!-- onclick="addBook() -->
<div id="main-page">
<h1>list</h1>
<div id="books-grid">
<table id="di-books">
<tr>
<th>Name</th>
<th>Author</th>
<th>Pages</th>
<th>Status</th>
<th></th>
</tr>
<tbody id="book-table-body"></tbody>
</table>
</div>
<button id="addBook" onclick="popForm(); toggle()"> </button>
</div>
<!-- JQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="app.js"></script>
</body>
</html>
CodePudding user response:
If you want to update the status of a book, you need a way to identify each book uniquely. Although it is possible to do this based on e.g. title or author, the reality is that those fields are not guaranteed to be unique.
Typically, you'd use the Id number of a database entry, but since you're using mock data that isn't realy an option here. We could e.g. use a UUID though:
let book = {
title: $("#title").val(),
author: $("#author").val(),
pages: $("#pages").val(),
status: $("#status").val(),
id: self.crypto.randomUUID() //Warning: might only work with HTTPS sites. Implement your own unique id in any way you please.
};
Now we have a unique id, we can use it to couple each status button with a unique book entry. Lets adjust the HTML:
const htmlBook = `
<tr>
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.pages}</td>
<td><button onclick="changeStatus('${book.id}')">${book.status}</button></td>
<td><button >delete</button></td>
</tr>
`;
and lets write the update function:
function changeStatus(id){
//step 1: find our book with the matching unique id
let book = books.filter(book => book.id === id)[0];
//Step 2: update the book its status
if (book.status === "read"){
book.status = "not read";
}
else if(book.status === "not read"){
book.status = "read"
}
//Step 3: update our table with the new data.
addBookToTable();
};
And voila, we're able to switch the status of any book!
I tried to stick as close to your code as possible so you'd better understand it, but a lot more improvements can be implemented, such as event listeners instead of explicitly calling the changeStatus
function, but I'll leave that as an exercise to you.
Full working example: codepen