#javascript #html #arrays #object
Вопрос:
У меня возникла проблема с удалением динамически созданного объекта dom из массива объектов. Проблема в том, что когда я удаляю какой-либо элемент из массива, остальные элементы после сращивания также удаляются. Насколько мне известно, это происходит из-за того, что индекс следующих элементов обновляется до предыдущего, и функция снова и снова удаляет элемент с одинаковым индексом.
Как я могу это исправить?(Код, загруженный в HTML и CSS вкл.) Является ли это рекомендуемым способом реализации этой функции?
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
Полный код
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
@font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!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>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Комментарии:
1. Каждый раз, когда вы добавляете элемент
myLib.forEach()
, вы добавляете более однойaddEventListener()
кнопки » Вкл.». Может быть, вместо этого попробуйте использовать уникальный идентификатор.2. что-то вроде
<div class="button" id="remove-btn-${book.id}">Remove</div>
тогда const removeButton = карта. Селектор запросов(`#удалить-btn-${book.id}`); removeButton.addEventListener(«щелчок», () => { Удалить книгу(book.id) })>3. Спасибо! Теперь я понимаю проблему с моим кодом. Я попытался реализовать ваш код, и он работает с несколькими исправлениями.
Ответ №1:
Проблема с вашим кодом заключается в том , что для каждой введенной карты populateBook
вы зацикливаете все предыдущие карты и добавляете прослушиватель событий щелчка, что означает, что вторая книга получает 2 копии этого обработчика, третья-3 и т. Д.
Вместо этого добавьте один обработчик событий для нажатия и соответствующей обработки:
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
});
Живой пример:
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
})
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
@font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!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>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Комментарии:
1. Спасибо, что объяснили! Теперь я понимаю, что я пытался сделать со своим кодом. Спасибо вам за решение. Кроме того, я думаю, вы имели в виду, что 2-я книга получает 2, а первая-3 экземпляра? Я ошибаюсь?