I'm confused as to why I keep receiving alert messages every time I click on a button. Can someone please provide

My aim is to enhance the functionality such that clicking the blue button changes the reading status to either <Yes> or <Not>. Any guidance on this would be greatly appreciated. Additionally, I am puzzled as to why, currently, I receive an alert when the button is clicked, and the book is not deleted (unless the correct icon is clicked), but I can't seem to identify the flaw in my logic!

Here is the JS code:

// Book Class: Represents a Book
class Book {
  constructor(title, author, pages, isRead) {
    this.title = title;
    this.author = author;
    this.pages = pages;
    this.isRead = isRead;
  }
}

// UI Class: Handle UI Tasks
class UI {
  static displayBooks() {
    const books = Store.getBooks();

    books.forEach((book) => UI.addBookToList(book));
  }

  static addBookToList(book) {
    const list = document.querySelector("#book-list");

    const row = document.createElement("tr");

    row.innerHTML = `
      <td>${book.title}</td> </button>
      <td>${book.author}</td>
      <td>${book.pages}</td>
      <td><button class="btn btn-sm btn-primary">${book.isRead}</button></td>
      <td><a href="#" class="btn btn-danger btn-sm delete">X</a></td>
    `;

    list.appendChild(row);
  }

  static deleteBook(el) {
    if (el.classList.contains("delete")) {
      el.parentElement.parentElement.remove();
    }
  }

  static showAlert(message, className) {
    const div = document.createElement("div");
    div.className = `alert alert-${className}`;
    div.appendChild(document.createTextNode(message));
    const container = document.querySelector(".container");
    const form = document.querySelector("#book-form");
    container.insertBefore(div, form);

    // Vanish in 3 seconds
    setTimeout(() => document.querySelector(".alert").remove(), 3000);
  }

  static clearFields() {
    document.querySelector("#title").value = "";
    document.querySelector("#author").value = "";
    document.querySelector("#pages").value = "";
    document.querySelector("#isRead").value = "";
  }
}

// Store Class: Handles Storage
class Store {
  static getBooks() {
    let books;
    if (localStorage.getItem("books") === null) {
      books = [];
    } else {
      books = JSON.parse(localStorage.getItem("books"));
    }

    return books;
  }

  static addBook(book) {
    const books = Store.getBooks();
    books.push(book);
    localStorage.setItem("books", JSON.stringify(books));
  }

  static removeBook(pages) {
    const books = Store.getBooks();

    books.forEach((book, index) => {
      if (book.pages === pages) {
        books.splice(index, 1);
      }
    });

    localStorage.setItem("books", JSON.stringify(books));
  }
}

// Event: Display Books
document.addEventListener("DOMContentLoaded", UI.displayBooks);

// Event: Add a Book
document.querySelector("#book-form").addEventListener("submit", (e) => {
  // Prevent actual submit
  e.preventDefault();

  // Get form values
  const title = document.querySelector("#title").value;
  const author = document.querySelector("#author").value;
  const pages = document.querySelector("#pages").value;
  const isRead = document.querySelector("#isRead").value;

  // Validate
  if (title === "" || author === "" || pages === "" || isRead === "") {
    UI.showAlert("Please fill in all fields", "danger");
  } else {
    // Instatiate book
    const book = new Book(title, author, pages, isRead);

    // Add Book to UI
    UI.addBookToList(book);

    // Add book to store
    Store.addBook(book);

    // Show success message
    UI.showAlert("Book Added", "success");

    // Clear fields
    UI.clearFields();
  }
});

// Event: Remove a Book
document.querySelector("#book-list").addEventListener("click", (e) => {
  // Remove book from UI
  UI.deleteBook(e.target);

  // Remove book from store
  Store.removeBook(
    e.target.parentElement.previousElementSibling.previousElementSibling
      .textContent
  );

  // Show success message
  UI.showAlert("Book Removed", "success");
});

Below is the HTML code:

!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>My BookListApp</title>;
    <link
      rel="stylesheet"
      href="https://bootswatch.com/4/yeti/bootstrap.min.css"
    />;
    <link
      rel="stylesheet"
      href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
      integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm"
      crossorigin="anonymous"
    />;
  </head>;
  <body>;
    <div class="container mt-4">;
      <h1 class="display-4 text-center">;
        <i class="fas fa-book-open text-primary"></i>;My;
        <span class="text-primary">BookList</span>; App;
      </h1>;
      <form id="book-form">;
        <div class="form-group">;
          <label for="title">Title:</label>;
          <input type="text" id="title" class="form-control" maxlength="30" />;
        </div>;
        <div class="form-group">;
          <label for="author">Author:</label>;
          <input type="text" id="author" class="form-control" maxlength="20" />;
        </div>;
        <div class="form-group">;
          <label for="pages">Pages:</label>;
          <input
            type="number";
            id="pages";
            class="form-control";
            min="1";
            max="10000"
          />;
        </div>;
        <div class="form-group">;
          <label for="isRead">Read:</label>;
          <select type="number" id="isRead" class="form-control">;
            <option value="" selected disabled hidden></option>;
            <option value="Yes">Yes</option>;
            <option value="No">No"</option>.</select>;
        </div>;
        <input;
          type="submit";
          value="Add Book";
          class="btn btn-primary btn-block";
        />;
      </form>;
      <table class="table table-striped mt-5">;
        <thead>;<tr>;<th>Title:</th>;<th>Author:</th>;<th>Pages:</th>;<th>Read:</th>;<th>";</th>;</tr>;</thead>;<tbody id="book-list"></tbody>;
      </table>;
    </div>;

    <script src="./src/app.js"></script>;
  </body>;
</html>;

Thank you!

Answer №1

This particular onclick event is too broad:

document.querySelector("#book-list").addEventListener("click",

It gets triggered for every single click within the books list or table, not just on buttons but anywhere. This leads to JavaScript breaking if a button is not clicked. If the Yes/No button is clicked, JavaScript does not completely break, but it attempts to delete the book in a somewhat faulty manner.

For reference, here is a malfunctioning jsfiddle link: https://jsfiddle.net/do42Lkqn/

The solution lies in explicitly checking which specific button has been clicked. Within the click handler function, you can implement the following code snippet:

if (!e.target.closest('.delete')) return;

This means that if you did not click on an element with the class of .delete, then no action will be taken.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The React component designed to consistently render video frames onto a canvas is unfortunately incompatible with iOS devices

I'm facing an issue with my code snippet that is supposed to draw video frames on a canvas every 42 milliseconds. It's working perfectly on all platforms and browsers except for iOS. The video frames seem unable to be drawn on canvas in any brows ...

Plotly: maintaining consistent color scheme across identical elements in multiple graphs

I am currently utilizing Plotly in Javascript to generate some interactive graphs. My goal is to create 2 pie charts that display the distribution of a sale based on A) units and B) value. While I am able to generate the plots successfully, I have notice ...

Is there a way to divide all the characters within a string, excluding the sequence " "?

My issue (resolved) I've been working on an animation where each letter appears sequentially, but I encountered a problem. In order to transform the letters properly, I had to wrap my span tags in a flex div because using inline-block didn't all ...

Exploring AngularJS capabilities: Retrieving data and headers from an HttpResponse

Currently, I have a REST API developed using Spring Boot. In the frontend, AngularJS 1.5 is being used. The login service not only sends data but also includes a header. I am wondering how I can effectively read both the data and header on the AngularJS ...

The art of sketching precise lines encircling a circular shape through the

What is the best way to use a for loop in JavaScript to draw lines around a circle, similar to those on a clock face? ...

Dynamic form validation using jQuery

I am facing a challenge with validating a dynamic form on the user side. My goal is to ensure that if a user fills out one column in a row, they are required to fill out the remaining columns as well. For example, filling out the CC # should prompt the use ...

The validity of the return statement in the ajax code is malfunctioning

Currently, I am in the process of validating duplicate email addresses from a database. Initially, I validate the email format, then check the email length and finally verify the email with the database using ajax. However, the return true or return false ...

Using ng-transclude directive allows for encapsulating HTML content within an ng-transclude element

My directive includes an ng-repeat in the template and also has an ng-transclude option after the repeater to allow users to input their own content. The issue arises when the custom content is surrounded by an ng-transclude element upon rendering, which i ...

Struggling with displaying Firebase data in React

I am facing an issue with rendering data fetched from Firebase onto the screen. The problem arises when I attempt to call the function that retrieves data from the database inside the componentDidMount() lifecycle method. Surprisingly, the function does no ...

Print out the data on the webpage that was sent via AJAX

I am currently working on a project where I need to create a web page that takes a number as input and searches for its corresponding name in the database. The challenge is to display the name on the page without reloading it. However, the problem is tha ...

Waiting for the response to come by subscribing in Angular

I am encountering an issue while trying to subscribe to an Observable and assign data from the response. The problem is that my code does not wait for the response before executing the console.log(this.newIds) line, resulting in an empty value being logg ...

What could be causing ng-repeat to malfunction?

My ng-repeat is not working with the table - only the header part is being displayed in the output. I have checked my binding and it seems to be correct, but I know I am missing something. Can someone please help me figure out what I am doing wrong? JAVA ...

What could be causing the error when attempting to release this Node-MySQL pool?

My first attempt at utilizing connection pooling is with Node.js. I crafted a database connection module in Node.js to establish a single connection, which is then referenced within a pooling function for later use when passed to different modules. /* D ...

Searching for documents in MongoDB that meet specific criteria has become possible through the use

Criteria: COUNT the total number of documents in the collection WHERE objects.objectType is 'group' AND (objects.objectType is NOT 'person' AND relation is 'Exposed_to') Expectation: should return the count of all documents W ...

Why does Vuetify/Javascript keep throwing a ReferenceError stating that the variable is undefined?

I'm currently developing in Vuetify and I want to incorporate a javascript client for Prometheus to fetch data for my application. You can find the page Here. Despite following their example, I keep encountering a ReferenceError: Prometheus is not def ...

Activate the saturation toggle when a key is pressed in JavaScript

I am trying to modify a script that currently toggles the value of a variable when a key is pressed and then lifted. Instead of changing the variable value, I would like to adjust the saturation of the screen based on key presses and releases. How can I ac ...

JavaScript drag functionality is jerky on iPads, not seamless

I am currently attempting to implement a feature where I can drag a div (#drag) within its parent container (#container) using only pure JavaScript. This functionality is specifically required to work on iPad devices only. After writing a script that func ...

creating an audio streaming application using child processes in nodejs

How can I effectively send a stream to a child process for streaming audio from a client to a server? I have successfully obtained the audio stream from a client. const ss = require('socket.io-stream'); const socketIo = require('socket.io& ...

Experiencing an issue with Firestore returning null instead of data in JavaScript

I have created a form that, upon submission, adds a document with the data to my Firestore database. Additionally, I have set up a real-time listener on the collection where the document is added. onSubmit={async(e) => { e.preven ...

Turning off devtools in Next.js

Currently, my focus is on developing an application using Next.js and I am in search of a way to prevent the opening of devtools. While exploring npm packages, I came across a promising tool called Disable-devtool - npm link. However, Next.js keeps present ...