简体   繁体   English

如何从本地存储阵列中删除唯一项? (到目前为止没有任何效果)

[英]How to delete unique item from local storage array? (Nothing has worked so far)

I am totally stuck.我完全被困住了。

I am working on the Library project from The Odin Project's JavaScript course, and I am trying to use localStorage so that the user can save their "library" to, well, their local storage.我正在研究 Odin 项目的 JavaScript 课程中的图书馆项目,我正在尝试使用 localStorage,以便用户可以将他们的“图书馆”保存到他们的本地存储中。

I've never been able to 100% successfully use localStorage before.我以前从未能够 100% 成功地使用 localStorage。 It seems simple enough, but for some reason, there was some kind of block in my brain about how to utilize it correctly.这看起来很简单,但由于某种原因,我的大脑中出现了某种关于如何正确使用它的障碍。 This time, however, I was able to get my program to be able to 1) save the user's data (via a button click), 2) load it correctly into the individual "cards" on the page, and 3) delete the entire storage (via another button click).然而,这一次,我能够让我的程序能够 1) 保存用户数据(通过单击按钮),2) 将其正确加载到页面上的各个“卡片”中,以及 3) 删除整个存储(通过单击另一个按钮)。 I am extremely excited about this... it feels so good to not know how to do something and then get it to work.我对此感到非常兴奋……不知道如何做某事然后让它发挥作用的感觉真好。

The one thing that has me totally confused is being able to delete a unique item from the localStorage array.让我完全困惑的一件事是能够从 localStorage 数组中删除一个唯一的项目。 When you click the "New Book" button on my page, a modal comes up, and you're required to enter a book's title, author, # of pages, and whether or not you have read it.当你点击我页面上的“新书”按钮时,会出现一个模式,你需要输入书名、作者、页数以及你是否阅读过这本书。 There's also an option to set the background color.还有一个选项可以设置背景颜色。 Clicking Submit will then generate a "card" with the book's information on it.单击“提交”将生成一张带有图书信息的“卡片”。

Each card has an X button in the corner.每张卡片的角落都有一个 X 按钮。 When the X button is clicked, the card is deleted from the page.单击 X 按钮时,卡片将从页面中删除。 I want it so that it (meaning that card's book's object) is also deleted from the local storage array (preferably without the user having to press the "Save to local storage" button again, although it isn't a dealbreaker).我想要它,以便它(意味着卡片的书的对象)也从本地存储阵列中删除(最好是用户不必再次按下“保存到本地存储”按钮,尽管它不是一个交易破坏者)。

Each time the user creates a new book, the book's details are saved as an object into an array called libraryBooks (which I then stringify so I can use localStorage).每次用户创建一本新书时,该书的详细信息都会作为 object 保存到一个名为 libraryBooks 的数组中(然后我将其字符串化,以便我可以使用 localStorage)。 From my understanding, in order to be able to delete individual parts of the array, I need to use code that is something like this:据我了解,为了能够删除数组的各个部分,我需要使用如下代码:

libraryBooks.splice(libraryBooks.indexOf(item), 1);

I think my issue is that I don't know how to get the item's index number.我认为我的问题是我不知道如何获取项目的索引号。 I'm not sure how to make it so that the program follows the instructions "When this X button is clicked, find that book's object in the array" if that makes sense.我不确定如何使程序遵循说明“单击此 X 按钮时,在数组中找到那本书的 object”(如果有意义的话)。 At this point, I've tried several things, and I'm just totally confused.在这一点上,我已经尝试了几件事,但我完全感到困惑。

So yeah.是的。 Any help would be wonderful.任何帮助都会很棒。 I've gone through and commented my code to try to help clarify what everything does, I've included the entire thing so that I don't potentially leave something important out related to my problem.我已经检查并评论了我的代码,以试图帮助阐明所有内容,我已经包含了整个内容,这样我就不会遗漏与我的问题相关的重要内容。 and so that it works in the code snippet, But in case it doesn't: here are links to the code repo and the live demo:这样它就可以在代码片段中工作,但以防万一:这里是代码库和现场演示的链接:

 // Link DOM elements const newBookBtn = document.querySelector('.newbook'); // New Book button const showBooks = document.querySelector('.show-books'); // div container const cardClose = document.querySelectorAll('.cardclose'); // button to close card const openEls = document.querySelectorAll("[data-open]"); // for popup boxes const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes) const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box const isVisible = "is-visible"; // for popup boxes const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes let libraryBooks = []; // localstorage buttons (save and delete) const saveStorage = document.getElementById('save-storage'); const deleteStorage = document.getElementById('delete-storage'); // Button event listeners saveStorage.addEventListener('click', updateLocalStorage); deleteStorage.addEventListener('click', deleteLocalStorage); submitBtn.addEventListener('click', addBookToLibrary); // If the 'books' key is empty, simply set libraryBooks to empty array. if (localStorage.getItem('books') === null) { libraryBooks = []; // Otherwise, set library books array to get items from the 'books' key } else { const booksFromStorage = JSON.parse(localStorage.getItem('books')); libraryBooks = booksFromStorage; } // The Book constructor function Book(title, author, pages, read) { this.title = title this.author = author this.pages = pages this.read = read this.info = function() { return `${this.title} by ${this.author}, ${this.pages} pages, ${read}`; } } // Add book to library function. function addBookToLibrary() { let bookTitle = document.querySelector('#book-title'); let bookAuthor = document.querySelector('#book-author'); let bookPages = document.querySelector('#book-pages'); let bookReadYes = document.querySelector('#bookreadyes') let bookReadNo = document.querySelector('#bookreadno'); let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty let bookRead; if (bookReadYes.checked) { bookRead = 'Read'; } else if (bookReadNo.checked) { bookRead = 'Not read'; } // Creating a new book object via the Book constructor let newBook = new Book(bookTitle.value, bookAuthor.value, bookPages.value, bookRead); // If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed. if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) { alertWords.textContent = 'Please fill in all fields.'; } else { alertWords.textContent = ''; document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal formBoxes.forEach(formBox => { formBox.value = ""; // Sets the form values so they're blank the next time the New Book button is pressed }); formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected) // Push the new book object into libraryBooks array libraryBooks.push(newBook); // The rest of the lines of code in this function create the actual book card on page const newCard = document.createElement('div'); const newCardTitle = document.createElement('h4'); const newCardAuthor = document.createElement('p'); const newCardPages = document.createElement('p'); const newCardRead = document.createElement('span'); newCardTitle.setAttribute('class', 'title-style'); newCardAuthor.setAttribute('class', 'author-style'); newCardPages.setAttribute('class', 'pages-style'); newCardRead.setAttribute('class', 'read-style'); newCard.classList.add('isVisible', 'cardbox', colorPicker()); showBooks.appendChild(newCard); for (let i = 0; i < libraryBooks.length; i++) { newCardTitle.innerHTML = `${libraryBooks[i].title}`; let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>"; newCardTitle.innerHTML += closeBtn; newCardAuthor.innerHTML = `by ${libraryBooks[i].author}`; newCardPages.innerHTML = `<strong>Pages</strong>: ${libraryBooks[i].pages}`; newCardRead.innerHTML = `<strong>Status</strong>: ${libraryBooks[i].read}`; } newCard.appendChild(newCardTitle); newCard.appendChild(newCardAuthor); newCard.appendChild(newCardPages); newCard.appendChild(newCardRead); } } // Stuff for popup capability for (const el of openEls) { el.addEventListener("click", function() { const modalId = this.dataset.open; document.getElementById(modalId).classList.add(isVisible); }); } for (const el of closeEls) { el.addEventListener("click", function() { this.parentElement.parentElement.parentElement.classList.remove(isVisible); }); } document.addEventListener("click", e => { if (e.target == document.querySelector(".modal.is-visible")) { document.querySelector(".modal.is-visible").classList.remove(isVisible); } }); // Keyboard shortvut for modal: ESC key to close document.addEventListener("keyup", e => { // if we press the ESC if (e.key == "Escape" && document.querySelector(".modal.is-visible")) { document.querySelector(".modal.is-visible").classList.remove(isVisible); } }); cardClose.forEach(card => { card.addEventListener('click', function() { this.parentNode.parentNode.removeChild(this.parentNode.parentNode); return false; }); }) // Switch function for setting the background color of the book's card function colorPicker() { switch (colorDropdown.value) { case 'red': return 'cardback-red'; break; case 'orange': return 'cardback-orange'; break; case 'yellow': return 'cardback-yellow'; break; case 'green': return 'cardback-green'; break; case 'blue': return 'cardback-blue'; break; case 'purple': return 'cardback-purple'; break; case 'dark': return 'cardback-dark'; break; case 'grey': return 'cardback-grey'; break; default: return 'cardback-white'; break; } } // Update local storage function updateLocalStorage() { localStorage.setItem('books', JSON.stringify(libraryBooks)); } // Delete local storage function deleteLocalStorage() { window.localStorage.clear(); showBooks.textContent = ""; } // Get localStorage data and set it to the variable "data" const data = JSON.parse(localStorage.getItem('books')); // Load the saved local storage objects into cards (almost identical to addBookToLibrary()) function loadLocalStorage(array, book) { let bookTitle; let bookAuthor; let bookPages; let bookRead; for (let i = 0; i < array.length; i++) { bookTitle = book.title; bookAuthor = book.author; bookPages = book.pages; bookRead = book.read; } // Create book card on page const newCard = document.createElement('div'); const newCardTitle = document.createElement('h4'); const newCardAuthor = document.createElement('p'); const newCardPages = document.createElement('p'); const newCardRead = document.createElement('span'); newCardTitle.setAttribute('class', 'title-style'); newCardAuthor.setAttribute('class', 'author-style'); newCardPages.setAttribute('class', 'pages-style'); newCardRead.setAttribute('class', 'read-style'); newCard.classList.add('isVisible', 'cardbox', colorPicker()); showBooks.appendChild(newCard); for (let i = 0; i < array.length; i++) { newCardTitle.innerHTML = `${bookTitle}`; let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>"; newCardTitle.innerHTML += closeBtn; newCardAuthor.innerHTML = `by ${bookAuthor}`; newCardPages.innerHTML = `<strong>Pages</strong>: ${bookPages}`; newCardRead.innerHTML = `<strong>Status</strong>: ${bookRead}`; } newCard.appendChild(newCardTitle); newCard.appendChild(newCardAuthor); newCard.appendChild(newCardPages); newCard.appendChild(newCardRead); } // Required in order to load saved books onto page for (let i = 0; i < data.length; i++) { loadLocalStorage(data, data[i]); }
 * { margin: 0; padding: 0; } body, html { display: flex; align-items: center; flex-direction: column; background-color: #cedee9; height: 100vh; padding: 10px; font-family: 'Rubik', sans-serif; } body { max-height: 100%; } input { padding: 5px; } label { margin-bottom: 5px; margin-top: 10px; font-size: .9rem; } input[type='text'] { font-size: .75rem; font-family: 'Poppins', sans-serif; }.radio-option1 { margin-right: 5px; } h4 { display: flex; justify-content: space-between; align-items: center; font-size: 1.2rem; }.close { background: none; font-size: 1.5rem; border: 0; margin-left: 10px; transition: 0.5s ease; }.close:hover { cursor: pointer; color: rgb(58, 84, 140); }.submitbtn { border: 1px solid rgb(58, 84, 140); color: white; background-color: rgb(58, 84, 140); padding: 8px 20px; margin: 0 auto; width: 100%; border-radius: 5px; transition: 0.5s ease; }.submitbtn:hover { cursor: pointer; background-color: rgb(40, 54, 85); border-color: rgb(40, 54, 85); }.book-form { display: flex; flex-direction: column; width: 200px; } /* Testing this comment */ input { margin-bottom: 10px; } p { margin-top: 5px; }.bookcard { width: 200px; display: flex; justify-content: flex-start; flex-direction: column; }.cardbox { background-color: white; width: 250px; margin: 10px; padding: 12px 20px 20px 20px; box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px; }.alertwords { color: #df0a0a; }.show-books { display: flex; flex-wrap: wrap; }.title-style { margin-bottom: -5px; }.author-style { font-size: .9rem; margin-bottom: 20px; }.pages-style, .read-style { font-size: .9rem; } option { font-family: 'Poppins', sans-serif; font-size: .8rem; } select { padding: 5px; font-family: 'Poppins', sans-serif; font-size: .8rem; } #option-red, .cardback-red { background-color: rgb(241, 191, 191); } #option-orange, .cardback-orange { background-color: #ffcb9a; } #option-yellow, .cardback-yellow { background-color: #fffda1; } #option-green, .cardback-green { background-color: #9cd6af; } #option-blue, .cardback-blue { background-color: #a1d3f0; } #option-purple, .cardback-purple { background-color: #e6c1ff; } #option-grey, .cardback-grey { background-color: #cfcfcf; } #option-dark, .cardback-dark { background-color: #282f52; color: white; } /* RESET RULES –––––––––––––––––––––––––––––––––––––––––––––––––– */ * { padding: 0; margin: 0; } a { color: inherit; text-decoration: none; }.close-modal { cursor: pointer; background: transparent; border: none; outline: none; font-size: inherit; }.btn-group { text-align: center; }.open-modal { font-weight: bold; background: steelblue; color: white; padding: 0.75rem 1.75rem; margin-bottom: 1rem; border-radius: 5px; border: 0; transition: 0.5s ease; }.open-modal:hover { background-color: rgb(40, 54, 85); cursor: pointer; }.open-modal:active { background-color: rgb(40, 54, 85); }.open-modal:focus { background-color: rgb(40, 54, 85); } /* MODAL –––––––––––––––––––––––––––––––––––––––––––––––––– */.modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; padding: 1rem; background: rgba(0, 0, 0, 0.781); cursor: pointer; visibility: hidden; opacity: 0; transition: all 0.35s ease-in; }.modal.is-visible { visibility: visible; opacity: 1; }.modal-dialog { position: relative; max-width: 800px; max-height: 80vh; border-radius: 5px; background: white; overflow: auto; cursor: default; }.modal-dialog>* { padding: 1rem; }.modal-header, .modal-footer { font-weight: 700; background: #a8c0f2; }.modal-header { display: flex; align-items: center; justify-content: space-between; font-size: 1.3rem; }.modal-header.close-modal { font-size: 1.5rem; }.modal p+p { margin-top: 1rem; } /* ANIMATIONS –––––––––––––––––––––––––––––––––––––––––––––––––– */ [data-animation].modal-dialog { opacity: 0; transition: all 0.5s var(--bounceEasing); } [data-animation].is-visible.modal-dialog { opacity: 1; transition-delay: 0.2s; } [data-animation="slideInOutDown"].modal-dialog { transform: translateY(100%); } [data-animation="slideInOutTop"].modal-dialog { transform: translateY(-100%); } [data-animation="slideInOutLeft"].modal-dialog { transform: translateX(-100%); } [data-animation="slideInOutRight"].modal-dialog { transform: translateX(100%); } [data-animation="zoomInOut"].modal-dialog { transform: scale(0.2); } [data-animation="rotateInOutDown"].modal-dialog { transform-origin: top left; transform: rotate(-1turn); } [data-animation="mixInAnimations"].is-visible.modal-dialog { animation: mixInAnimations 2s 0.2s linear forwards; } [data-animation="slideInOutDown"].is-visible.modal-dialog, [data-animation="slideInOutTop"].is-visible.modal-dialog, [data-animation="slideInOutLeft"].is-visible.modal-dialog, [data-animation="slideInOutRight"].is-visible.modal-dialog, [data-animation="zoomInOut"].is-visible.modal-dialog, [data-animation="rotateInOutDown"].is-visible.modal-dialog { transform: none; } @keyframes mixInAnimations { 0% { transform: translateX(-100%); } 10% { transform: translateX(0); } 20% { transform: rotate(20deg); } 30% { transform: rotate(-20deg); } 40% { transform: rotate(15deg); } 50% { transform: rotate(-15deg); } 60% { transform: rotate(10deg); } 70% { transform: rotate(-10deg); } 80% { transform: rotate(5deg); } 90% { transform: rotate(-5deg); } 100% { transform: rotate(0deg); } } /* FOOTER –––––––––––––––––––––––––––––––––––––––––––––––––– */ footer { display: flex; align-items: center; justify-content: center; }.page-footer { position: fixed; bottom: 0; }.page-footer span { color: #e31b23; }
 <,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> <script src="https.//kit.fontawesome.com/2b4114baf6:js" crossorigin="anonymous"></script> <.-- Google fonts --> <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=Montserrat,ital;wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800:1,900&family=Noto+Sans,ital;wght@0,400;0,700;1,400:1,700&family=Open+Sans,ital;wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700:1,800&family=Poppins,ital;wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800:1;900&family=Roboto+Slab;wght@100;200;300;400;500;600;700:800,900&family=Roboto,ital;wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700:1,900&family=Rubik,ital;wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800:1,900&family=Source+Sans+Pro,ital;wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700.1:900&display=swap" rel="stylesheet"> <.-- CSS Stylesheet --> <link href="styles.css" rel="stylesheet"> <.-- jQuery --> <script src="https.//ajax.googleapis.com/ajax/libs/jquery/3:6:0/jquery:min?js"></script> </head> <body> <h1>Library</h1> <div class="btn-group"> <button type="button" class="open-modal" data-open="modal1"> New Book </button> </div> <div class="btn-group"> <button class="open-modal" id="save-storage"> Save local storage </button> <button class="open-modal" id="delete-storage"> Delete local storage </button> </div> <:-- Library books container --> <div class="show-books"> </div> <:-- Modal code (popup box for new book form) --> <div class="modal" id="modal1" data-animation="slideInOutLeft"> <div class="modal-dialog"> <header class="modal-header"> Book Details <button class="close-modal" aria-label="close modal" data-close> ✕ </button> </header> <section class="modal-content"> <form class="bookcard"> <label>Title:</label> <input type="text" class="form-box" id="book-title" placeholder="Fight Club"> <label>Author.</label> <input type="text" class="form-box" id="book-author" placeholder="Chuck Palahniuk"> <label># of Pages.</label> <input type="number" class="form-box" id="book-pages" placeholder="208" min="1" max="14000"> <label>Have you read this book?:</label> <div class="radiobox"> <label class="radio-option1"><input type="radio" name="read" value="yes" id="bookreadyes" class='form-radio' checked> Yes</label> <label class="radio-option2"><input type="radio" name="read" value="no" id="bookreadno" class='form-radio'> No</label> </div> <label>Select card color (optional):</label> <select class="colorpicker"> <option value='default' selected disabled>Please select one</option> <option value="white">White (Default)</option> <option value="blue" id="option-blue">Blue</option> <option value="purple" id="option-purple">Purple</option> <option value="green" id="option-green">Green</option> <option value="grey" id="option-grey">Grey</option> <option value="red" id="option-red">Red</option> <option value="orange" id="option-orange">Orange</option> <option value="yellow" id="option-yellow">Yellow</option> <option value="dark" id="option-dark">Dark mode</option> </select> <p class="alertwords"></p> </form> </section> <footer class="modal-footer"> <button class="submitbtn">Submit</button> </footer> </div> </div> <footer class="page-footer"> <small>Made with <span>❤</span> by <a href="http://georgemartsoukos.com/" target="_blank">Sara Dunlop</a> </small> </footer> <!-- JS script --> <script src="script2.js"></script> </body> </html>

Here is the simple fix for you这是适合您的简单修复

let closeBtn = `<button type='button' class='close-default' onclick='libraryBooks.splice(libraryBooks.findIndex((book) => book.title === "${bookTitle}" && book.author === "${bookAuthor}"), 1);'>x</button>`;

i is getting from your array list i从你的数组列表中获取

Just in case you don't understand what is template literals, I share the link here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals以防万一你不明白什么是模板文字,我在这里分享链接https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

I also added an extra fix for your deleteLocalStorage .我还为您的deleteLocalStorage添加了额外的修复。 You need to clean up your libraryBooks array too.您还需要清理libraryBooks数组。

function deleteLocalStorage() {
  libraryBooks = [] //here is the fix to clean up your array
  window.localStorage.clear();
  showBooks.textContent = "";
}

You can check my fixes here你可以在这里查看我的修复

https://jsfiddle.net/otywdekm/4/ https://jsfiddle.net/otywdekm/4/

Well, I made a good couple changes to your script and it works The main problem was that your close button wasn't given a listener that would be able to remove the book it's related to, solved that and also for fun added logic to automatically save to localStorage好吧,我对你的脚本做了一些很好的修改并且它有效主要问题是你的关闭按钮没有给一个监听器,它可以删除它相关的书,解决了这个问题,并且为了好玩自动添加逻辑保存到localStorage

The code's below but there's also a link to a working example代码在下面,但还有一个指向工作示例的链接

// Link DOM elements
const newBookBtn = document.querySelector('.newbook'); // New Book button
const showBooks = document.querySelector('.show-books'); // div container
const cardClose = document.querySelectorAll('.cardclose'); // button to close card
const openEls = document.querySelectorAll("[data-open]"); // for popup boxes
const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes
const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes)
const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box
const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box
const isVisible = "is-visible"; // for popup boxes
const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes
let libraryBooks = [], automaticallyUpdate = true; //change automaticallyUpdate to false to prevent automatic saving(on changes)


// localstorage buttons (save and delete)
const saveStorage = document.getElementById('save-storage');
const deleteStorage = document.getElementById('delete-storage');

// Button event listeners
saveStorage.addEventListener('click', updateLocalStorage);
deleteStorage.addEventListener('click', deleteLocalStorage);
submitBtn.addEventListener('click', addBookToLibrary);


// The Book constructor
function Book({title, author, pages, read, color}) {
  this.title = String(title)
  this.author = String(author)
  this.pages = Number(pages)
  this.read = read?"Read":"Not Read"
  this.color = String(color)
  this.info = function() {
    return `${this.title} by ${this.author}, ${this.pages} pages, ${this.read} with color ${this.color}`;
  }
}

//returns your close button with a listener that actually removes the book
//"<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
function closeBar(book){
  let btn=document.createElement('button')
  btn.className='close-default'
  btn.innerHTML='x' //wow almost forgot this
  btn.addEventListener('click',()=>{
    $(btn).parent().parent().remove()
    console.log(book,libraryBooks,libraryBooks.indexOf(book))
    libraryBooks.splice(libraryBooks.indexOf(book),1)
    if(automaticallyUpdate){updateLocalStorage()} //automatic saving
  })
  return btn //the button is returned to be placed in its arrangement
}


// Add book to library function.
function addBookToLibrary() {
  let color = colorDropdown.value;
  let bookTitle = document.querySelector('#book-title');
  let bookAuthor = document.querySelector('#book-author');
  let bookPages = document.querySelector('#book-pages');
  let bookRead = document.querySelector('#bookreadyes').checked; //true if checked, false if not checked
  let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty
    
  // Creating a new book object via the Book constructor
  let newBook = new Book({title:bookTitle.value, author:bookAuthor.value, pages:bookPages.value, read:bookRead, color});


  // If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed.
  if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) {
      alertWords.textContent = 'Please fill in all fields.';
  } else {
      alertWords.textContent = '';
      document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal
      formBoxes.forEach(formBox => {
          formBox.value = "";           // Sets the form values so they're blank the next time the New Book button is pressed
      });
      formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected)

      // Push the new book object into libraryBooks array
      libraryBooks.push(newBook);
      
      // The rest of the lines of code in this function create the actual book card on page
      const newCard = document.createElement('div');
      const newCardTitle = document.createElement('h4');
      const newCardAuthor = document.createElement('p');
      const newCardPages = document.createElement('p');
      const newCardRead = document.createElement('span');

      newCardTitle.setAttribute('class', 'title-style');
      newCardAuthor.setAttribute('class', 'author-style');
      newCardPages.setAttribute('class', 'pages-style');
      newCardRead.setAttribute('class', 'read-style');

      newCard.classList.add('isVisible', 'cardbox', colorPicker(newBook.color));
      showBooks.appendChild(newCard);

      const {title,author,pages,read}=newBook
      newCardTitle.innerHTML = `${title}`;
      let closeBtn = closeBar(newBook)
      newCardTitle.appendChild(closeBtn);
      newCardAuthor.innerHTML = `by ${author}`;
      newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
      newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
      
      
      newCard.appendChild(newCardTitle);
      newCard.appendChild(newCardAuthor);
      newCard.appendChild(newCardPages);
      newCard.appendChild(newCardRead);
      
      if(automaticallyUpdate){updateLocalStorage()} //automatic saving

   }
}



// Stuff for popup capability

for (const el of openEls) {
    el.addEventListener("click", function() {
        const modalId = this.dataset.open;
        document.getElementById(modalId).classList.add(isVisible);
    });
}

for (const el of closeEls) {
    el.addEventListener("click", function() {
        this.parentElement.parentElement.parentElement.classList.remove(isVisible);
    });
}

document.addEventListener("click", e => {
    if (e.target == document.querySelector(".modal.is-visible")) {
        document.querySelector(".modal.is-visible").classList.remove(isVisible);
    }
});

// Keyboard shortvut for modal: ESC key to close
document.addEventListener("keyup", e => {
    // if we press the ESC
    if (e.key == "Escape" && document.querySelector(".modal.is-visible")) {
        document.querySelector(".modal.is-visible").classList.remove(isVisible);
    }
});

cardClose.forEach(card => {
    card.addEventListener('click', function() {
        this.parentNode.parentNode.removeChild(this.parentNode.parentNode);
        return false;
    });
})


// Switch function for setting the background color of the book's card
let colors = {red:1, orange:1, yellow:1, green:1, blue:1, purple:1, dark:1, grey:1}
//the above variable saves a lot of lines in the colorPicker function
function colorPicker(color) {
    if(!colors[color]){return 'cardback-white'}
    return 'cardback-'+color
}


// Update local storage
function updateLocalStorage() {
    localStorage.setItem('books', JSON.stringify(libraryBooks));
}


// Delete local storage
function deleteLocalStorage() {
    window.localStorage.clear();
    showBooks.textContent = "";
}


// Get localStorage data and set it to the variable "data"
const data = JSON.parse(localStorage.getItem('books'))||[]
// the "||" in case there was nothing stored yet and it prevents an error from reading map from null
.map(book=>new Book( book )) //convert localStorage data to a list of "Book"s

// Load the saved local storage objects into cards (almost identical to addBookToLibrary())
function loadLocalStorage({title,author,pages,read,color}) {
    // EDIT: the for loops in this function are seemingly useless    
    var newBook=arguments[0]
    libraryBooks.push(newBook)
    // Create book card on page
    const newCard = document.createElement('div');
    const newCardTitle = document.createElement('h4');
    const newCardAuthor = document.createElement('p');
    const newCardPages = document.createElement('p');
    const newCardRead = document.createElement('span');

    newCardTitle.setAttribute('class', 'title-style');
    newCardAuthor.setAttribute('class', 'author-style');
    newCardPages.setAttribute('class', 'pages-style');
    newCardRead.setAttribute('class', 'read-style');
    
    newCard.classList.add('isVisible', 'cardbox', colorPicker(color));
    showBooks.appendChild(newCard);
     
    
    newCardTitle.innerHTML = `${title}`;
    const closeBtn = closeBar(newBook);
    newCardTitle.appendChild(closeBtn);
    newCardAuthor.innerHTML = `by ${author}`;
    newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
    newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
     
     newCard.appendChild(newCardTitle);
     newCard.appendChild(newCardAuthor);
     newCard.appendChild(newCardPages);
     newCard.appendChild(newCardRead);
}

// Required in order to load saved books onto page
for(let i = 0; i < data.length; i++) {
    loadLocalStorage(data[i]);
}

I checked out your demo, and what I think you can do is, while saving a book object you can pass in an property called id to identify the exact book from the collection.我检查了你的演示,我认为你可以做的是,在保存一本书 object 时,你可以传入一个名为 id 的属性来识别集合中的确切书籍。

So when you click on the x button you will get the details of that book which now has an id and you can filter it out.因此,当您单击 x 按钮时,您将获得该书的详细信息,该书现在有一个 ID,您可以将其过滤掉。

Something as follows -内容如下-

const filteredBooks = existingBooks.filter((el) => el.id !== book.id);
localstorage.setItem('books', filteredBooks);

Hope that works for you.希望对你有用。

You Problem in delete specific book card您在删除特定书卡时遇到问题

libraryBooks.splice(libraryBooks.indexOf(item), 1);

Thats not correct because那是不正确的,因为

indexOf() compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator). indexOf() 使用严格相等(与 === 或三等号运算符使用的相同方法)将 searchElement 与 Array 的元素进行比较。

libraryBooks.indexOf always results in -1 libraryBooks.indexOf 总是导致 -1

the solution you can add index when you create book in book constructor and increment it in every book在书籍构造函数中创建书籍并在每本书中递增它时可以添加索引的解决方案

[
{
index: 0
author: "00"
pages: "200"
read: "Read"
title: "00"
}
]

when create new object set index++ to able get the object directly and when delete当创建新的 object 设置 index++ 时,可以直接获取 object,当删除时

function deleteBook(book){
   libraryBooks.splice(book.index , 1)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM