Home > Software design >  How to modify checked status of user input on individual card without modifying all cards (when usin
How to modify checked status of user input on individual card without modifying all cards (when usin

Time:06-29

Library project. When I add a new book to my library, the newest book's read status changes all the book's read statuses to the latest instead of only modifying the book that is currently being added. I believe this is because I remove all the cards at the beginning of the displayBooks() function. I do this to avoid duplicates when the for loop runs through the library again. I am thinking there is a way to either prevent the for loop from adding any duplicates so that I don't need to run the removeCards() function or that I can somehow store the read status of each card so that it won't change when a new book is added.

I've tried breaking apart the individual book elements into an array so that I could store the read status values but then I started running into other issues and made somewhat of a mess. Here is my previous code from before I made those changes. The read status is the only issue I am having. Hoping someone can point me in the right direction.

JS

let addBtn = document.getElementById('addBtn');
let cards = document.querySelector('.cards');

let modal = document.getElementById("myModal");
let openModal = document.getElementById("openModal");
let span = document.getElementsByClassName("close")[0];

openModal.onclick = function() {
  modal.style.display = "grid";
}

span.onclick = function() {
  modal.style.display = "none";
}

window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}

let myLibrary = [];

function getBookFromInput() {
    const title = document.getElementById('title').value;
    const author = document.getElementById('author').value;
    const pages = document.getElementById('pages').value;
    const newBook = new Book(title, author, pages, read);
    newBook.info();
}

function Book(title, author, pages, read) {
    this.title = title;
    this.author = author;
    this.pages = pages;
    this.read = read;
    this.info = function() {
        let book = `Title: ${title}\nAuthor: ${author}\nPages: ${pages}`
        addBookToLibrary(book);
    }
}

function addBookToLibrary(book) {
    myLibrary.push(book);
    displayBooks();
}

function displayBooks() {
    removeCards()
    
    let read;
    if (document.getElementById('read').checked == true) {
        read = 'Read!';
    } else {
        read = 'Not read yet!';
    }

    let books = myLibrary;
    for (let book of books) {
        let card = document.createElement('div');
        cards.appendChild(card).innerText = book;
        card.classList.add('card');
        card.setAttribute('id', myLibrary.indexOf(book));

        let readBtn = document.createElement('button');
        card.appendChild(readBtn).innerText = read;
        readBtn.classList.add('readBtn');
        readBtn.setAttribute('id', 'readBtn')
        readBtn.onclick = function() {
            if (readBtn.innerText === "Read!") {
                readBtn.innerText = "Not read yet!";
              } else {
                readBtn.innerHTML = "Read!";
              }
        }
        
        let removeBtn = document.createElement('button');
        card.appendChild(removeBtn).innerText = 'Remove';
        removeBtn.classList.add('removeBtn')
        removeBtn.setAttribute('id', 'removeBtn');

        removeBtn.onclick = function() {
            myLibrary.splice(myLibrary.indexOf(book),1);
            displayBooks();
        }
    }
}

function removeCards() {
    while (cards.lastChild) {
        cards.removeChild(cards.lastChild);
    }
}

HTML

<!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>Library</title>
    <link rel="stylesheet" href="style.css">
    <script src="script.js" defer></script>
</head>
<body>
    <div >
        <h1>Library</h1>
    </div>
    <div >
        <button id="openModal"> </button>
        <div  id="myModal">
            <div >
                <span >&times;</span>
                <div >
                    <label for="title">Title:</label>
                    <input id="title" type="text">
                </div>
                <div >
                    <label for="author">Author:</label>
                    <input id="author" type="text">
                </div>
                <div >
                    <label for="pages">Pages:</label>
                    <input id="pages" type="number">
                </div>
                <div >
                    <label for="read">Read?</label>
                    <input id="read" type="checkbox">
                </div>
                <button id="addBtn" onclick="getBookFromInput()">Add Book</button>
            </div>
        </div>
        <div ></div>
    </div>
</body>
</html>

CSS

:root {
    font-size: 16px;
    font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}

body {
    background-color: whitesmoke;
    display: flex;
    flex-direction: column;
    margin: 0;
}

.container {
    display:flex;
    flex-direction: column;
    gap: 50px;
    background-color: whitesmoke;
    align-items: center;
}

.header {
    background-color: white;
    border-bottom: 5px whitesmoke inset;
    padding-left: 30px;
}

input {
    width: 300px;
}

.newBook {
    font-weight: 700;
    display: flex;
    flex-direction: column
} 

.cards {
    display: grid;
    grid-template-columns: repeat(4, minmax(300px, 1fr));
    gap: 50px;
}

.card {
    border: solid orangered 5px;
    background-color: orange;
    padding: 20px;
    font-size: 2rem;
    font-weight: 500;
    display: flex;
    flex-direction: column;
} 

#openModal, #addBtn {
    border: solid orangered;
    background-color: orangered;
    color: white;
    padding: 10px;
}

#addBtn {
    justify-self: center;
    margin-top: 10px;
    border-radius: 10px;
    max-width: 200px;
    font-size: 1.25rem;
    font-weight: 700;
}

#openModal {
    border-radius: 50%;
    height: 50px;
    width: 50px;
    font-size: 3rem;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 15px;;
}

#removeBtn {
    display: flex;
    justify-self: flex-end;
    align-self: flex-end;
    margin-top: 15px;
    margin-left: 15px;
    background-color: #fefefe;
    color: black;
    border: solid black 2px;
    font-weight: 600;
}

.readBtn {
    background-color: y;
    display: flex;
    justify-self: flex-start;
    align-self: flex-start;
    margin-left: 0px;
    margin-top: 15px;
    color: black;
    border: solid black 2px;
    border-radius: 10px;
    font-size: 1.5rem;
    font-weight: 600;
    max-width: 200px;
}

.modal {
    display: none; 
    position: fixed; 
    z-index: 1; 
    left: 0;
    top: 0;
    width: 100%; 
    height: 100%; 
    overflow: auto; 
    background-color: rgb(0,0,0); 
    background-color: rgba(0,0,0,0.4); 
}

.modal-content {
    display: grid;
    grid-template-rows: repeat(6, 1fr);
    background-color: #fefefe;
    margin: 15% auto; 
    padding: 20px;
    border: 1px solid #888;
    width: 35%; 
}
  
.close {
    color: #aaa;
    margin-left: 95%;
    font-size: 28px;
    font-weight: bold;
}
  
.close:hover, .close:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

.pages, .title, .author, .read {
    display: grid;
    grid-template-columns: 1fr 5fr;
    padding: 10px;
}

label {
    font-weight: 700;
}

CodePudding user response:

  1. In your Book constructor, store a reference to the "read" status, and pass it on to addBookToLibrary, like so:
function Book(title, author, pages, read) {
    this.title = title;
    this.author = author;
    this.pages = pages;
    this.isRead = read;
    this.info = function() {
        let book = `Title: ${title}\nAuthor: ${author}\nPages: ${pages}`
        addBookToLibrary(book, this.isRead);
    }
}
  1. In your addBookToLibrary function you need provide the "read" status alongside your book information, something as such:
function addBookToLibrary(book, read) {
  console.log('added book, ', book)
    myLibrary.push({info: book, isRead: read});
    displayBooks();
}
  1. Then, add the appropriate calls to book.info and book.isRead to update your DOM accordingly, like so:
function displayBooks() {
    removeCards()
    let books = myLibrary;
    for (let book of books) {
        let card = document.createElement('div');
        cards.appendChild(card).innerText = book.info;
        card.classList.add('card');
        card.setAttribute('id', myLibrary.indexOf(book.info));

        let readBtn = document.createElement('button');
        card.appendChild(readBtn).innerText = book.isRead ? 'Read!' : 'Not read yet!';
        readBtn.classList.add('readBtn');
        readBtn.setAttribute('id', 'readBtn')
        readBtn.onclick = function() {
            if (readBtn.innerText === "Read!") {
                readBtn.innerText = "Not read yet!";
              } else {
                readBtn.innerHTML = "Read!";
              }
        }
        
        let removeBtn = document.createElement('button');
        card.appendChild(removeBtn).innerText = 'Remove';
        removeBtn.classList.add('removeBtn')
        removeBtn.setAttribute('id', 'removeBtn');

        removeBtn.onclick = function() {
          console.log(myLibrary)
            myLibrary.splice(myLibrary.indexOf(book.info),1);
            displayBooks();
        }
    }
}

CodePudding user response:

Deleting the list of displayed books and rewriting it is not a problem. The problem is when iterating through your library of books and determining the read button text for each book, you're getting the current value of the Read? checkbox: if (document.getElementById('read').checked == true) {, which is simply the read status of the last book entered.

Instead, to keep track of each books read status, add the checked status of the Read? checkbox when you add the new book to the library along with the rest of the book information: const read = document.getElementById('read').checked;

Also, to make your library more adaptable and accessible, save the Book object to the library: myLibrary.push(new Book(title, author, pages, read));, rather than a text string.

Then, when writing your list of books use book.title, book.author, book.pages, and , book.read to display the corresponding book information:

document.getElementById('addBtn').addEventListener("click", getBookFromInput);
let cards = document.querySelector('.cards');

let myLibrary = [];

function getBookFromInput(evt) {
  const title = document.getElementById('title').value;
  const author = document.getElementById('author').value;
  const pages = document.getElementById('pages').value;
  const read = document.getElementById('read').checked;
  myLibrary.push(new Book(title, author, pages, read));
  displayBooks();
}

function Book(title, author, pages, read) {
  this.title = title;
  this.author = author;
  this.pages = pages;
  this.read = read;
}

function displayBooks() {
  removeCards()

  for (let book of myLibrary) {
    let card = document.createElement('div');
    cards.appendChild(card).innerText = `Title: ${book.title}\nAuthor: ${book.author}\nPages: ${book.pages}\n`;
    card.classList.add('card');
    card.setAttribute('id', myLibrary.indexOf(book));

    let read;
    if (book.read === true) {
      read = 'Read!';
    } else {
      read = 'Not read yet!';
    }

    let readBtn = document.createElement('button');
    card.appendChild(readBtn).innerText = read;
    readBtn.classList.add('readBtn');
    readBtn.onclick = function() {
      if (readBtn.innerText === "Read!") {
        myLibrary[myLibrary.indexOf(book)].read = false;
        readBtn.innerText = "Not read yet!";
      } else {
        myLibrary[myLibrary.indexOf(book)].read = true;
        readBtn.innerHTML = "Read!";
      }
    }

    let removeBtn = document.createElement('button');
    card.appendChild(removeBtn).innerText = 'Remove';
    removeBtn.classList.add('removeBtn')

    removeBtn.onclick = function() {
      myLibrary.splice(myLibrary.indexOf(book),1);
      displayBooks();
    }
  }
}

function removeCards() {
  while (cards.lastChild) {
    cards.removeChild(cards.lastChild);
  }
}
<div >
  <h1>Library</h1>
</div>
<div >
  <div >
    <label for="title">Title:</label>
    <input id="title" type="text">
  </div>
  <div >
    <label for="author">Author:</label>
    <input id="author" type="text">
  </div>
  <div >
    <label for="pages">Pages:</label>
    <input id="pages" type="number">
  </div>
  <div >
    <label for="read">Read?</label>
    <input id="read" type="checkbox">
  </div>
  <button id="addBtn">Add Book</button>
  <div ></div>
</div>

I also removed the book button ids, for example: removeBtn.setAttribute('id', 'removeBtn'); since ids are necessarily unique and you're already applying a class with the same name.

  • Related