Проблема с удалением объекта из массива объектов

#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 экземпляра? Я ошибаюсь?