Upon the second click, the addEventListener function is triggered

When using the window.addEventListener, I am encountering an issue where it only triggers on the second click. This is happening after I initially click on the li element to view the task information, and then click on the delete button which fires the event for reading the task information. The delete button is placed within the list element.

The list element has a click handler specifically for reading the task information.

The delete button has its own handler for removing the task. I make sure to attach these events with stop event propagation to prevent triggering events on the parent elements.

The main problem here is that addEventListener only responds after the second click.

Another issue arises when removing the eventListeners altogether: clicking on the delete button also ends up triggering the event to read the task information.

I am seeking some genuine solutions to resolve these issues. Any help would be greatly appreciated. Thank you!

Check out the working demo => Working Demo

Here's my code:

window.deleteTask = function deleteTask(id) {
 const deleteButton = document.getElementById(id);
  deleteButton.addEventListener(
    "click",
    (event) => {
      // event.preventDefault();
      let taskAppObj = new ToDoConstructor();
      taskAppObj.removetask(id);
      event.stopPropagation();
    },
    false
  );
};

window.getTaskInfo = function getTaskInfo(id) {
  const li = document.getElementById(`data-${id}`);
  li.addEventListener("click", (event) => {
    let taskAppObj = new ToDoConstructor();
    let task = window.storeContext.find((task) => task.id === id);
    taskAppObj.readTheTask(task);
  });
};

Answer №1

setTimeout(function () {
  start();
}, 1000);

const TaskConstructor = function () {
  this.taskList = [
    {
      id: 1,
      title: "Task 1",
      description: "Running",
      priority: 1
    },
    {
      id: 2,
      title: "Task 2",
      description: "Reading",
      priority: 2
    },
    {
      id: 3,
      title: "Task 3",
      description: "Walking",
      priority: 3
    },
    {
      id: 4,
      title: "Task 4",
      description: "Eating",
      priority: 4
    }
  ];
  this.displayTask = function (params) {
    const taskDiv = document.getElementById("task-info");
    const titleSpan = document.getElementById("titleSpan");
    const descrSpan = document.getElementById("descrSpan");
    const prioritySpan = document.getElementById("prioritySpan");

    taskDiv.setAttribute("class", "visible");

    titleSpan.innerHTML = params.title;
    descrSpan.innerHTML = params.description;
    prioritySpan.innerHTML = params.priority;
  };
  this.removeTask = function (id) {

    if (window.storeContext.length > 0) {
      this.taskList = [...window.storeContext];
    }

    this.taskList = this.taskList.filter((element) => element.id !== id);
    
    window.storeContext = [...this.taskList];

    retrieveTaskList();
  };
};

function start() {
  const taskManager = new TaskConstructor();
  window.storeContext = [...taskManager.taskList];
  retrieveTaskList();
}
document.addEventListener("DOMContentLoaded", function () {
  const taskListOfUl = document.getElementById("task-elements");

  taskListOfUl.addEventListener("click", function (event) {
    if (event.target.tagName === "BUTTON") {
      const id = event.target.id;
      deleteSelectedTask(id);
    } else if (event.target.tagName === "LI") {
      const id = event.target.id.split("-")[1]; 
      viewTaskInfo(id);
    }
  });
});

function deleteSelectedTask(id) {
  let taskManager = new TaskConstructor();
  taskManager.removeTask(id);
}

function viewTaskInfo(id) {
  let taskManager = new TaskConstructor();
  let task = window.storeContext.find((task) => task.id == id);
  taskManager.displayTask(task);
}

function retrieveTaskList() {
  let tasks = [...window.storeContext];
  
  const taskListOfUl = document.getElementById("task-elements");
  taskListOfUl.innerHTML = "";

  if (tasks && tasks.length > 0) {
    tasks.forEach((task) => {
      const listItem = document.createElement("li");
      listItem.id = `data-${task.id}`;

      const taskTitle = document.createElement("span");
      taskTitle.innerText = task.title;

      const deleteButton = document.createElement("button");
      deleteButton.innerText = "x";

      listItem.addEventListener("click", function (event) {
     
        if (event.target.tagName === "SPAN") {
          viewTaskInfo(task.id);
        }
      });

      deleteButton.addEventListener("click", function (event) {
        event.stopPropagation(); 
        deleteSelectedTask(task.id);
      });

      listItem.appendChild(taskTitle);
      listItem.appendChild(deleteButton);
      taskListOfUl.appendChild(listItem);
    });
  }
}

document.getElementById("app").innerHTML = `
<h1>Tasks</h1>
<div class=task-display-div>
  <div class=task-col>
    <header>Task List</header>
    <div>
      <ul id=task-elements>
      </ul>
    </div>
  </div>
  <div class=task-col>
    <header>
      Task Details:
    </header>
    <div id=task-info class=hidden>
      <div>
        <h5>Task Title: </h5><SPAN id=titleSpan></SPAN>
      </div>
      <div>
        <h5>Task Details: </h5><SPAN id=descrSpan></SPAN>
      </div>
      <div>
        <h5>It is a prio <SPAN id=prioritySpan></SPAN> task.</h5>
      </div>
  </div>
</div>
`;
body {
  font-family: sans-serif;
}

.task-display-div {
  display: flex;
  width: 90%;
}

.task-col {
  padding: 0px 10px;
  border-right: 1px solid;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app"></div>

  </body>
</html>

Making some updates to the code makes it work with just one click (see full page for result)

Answer №2

It is recommended to use createElement instead of a string literal and not pass an initial argument in any Event listener function; the event argument will always be provided by default, which is considered good practice.

function displayTaskList() {
  let tasks = [...window.storeContext];
  console.log("tasks", tasks);
  const taskListUl = document.getElementById("task-elements");
  taskListUl.innerHTML =
    tasks && tasks.length > 0
      ? tasks
          .map(
            (task) =>
              `<li id=data-${task.id} onclick="retrieveTaskInformation(event)" key=${task.id}>
                ${task.title}
                <button id=${task.id} onclick="removeTask(event)">X</button>
              </li>`
          )
          .join("<br/>")
      : null;
}

Retrieve Task Information

window.retrieveTaskInformation = function retrieveTaskInformation(event) {
  const id = event.currentTarget.id.split('-')[1];
  let taskAppObj = new ToDoConstructor();
  let task = window.storeContext.find((task) => task.id === Number(id));
  taskAppObj.readTheTask(task);
};

Delete Task

window.removeTask = function removeTask(event) {
  event.stopPropagation();
  const id = event.currentTarget.id;
  console.log("This is the delete function!");
  let taskAppObj = new ToDoConstructor();
  taskAppObj.removetask(Number(id));
}

This code has also been tested in CodeSandbox.

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

Determine the presence of a JSON Value/Array in a web application using JavaScript and visualize the information in a dynamic

Utilizing the Ticketmaster API has provided me with a sample dataset from their platform, Data 1 - Including information on "presales." "sales": { "public": { "startDateTime": "2019-11 ...

Prevent scrolling on Bootstrap columns

My webpage is built with Bootstrap and I have a layout with 4 columns on the left and 6 on the right. I want to prevent the 4 columns on the left from scrolling. How can I achieve this? I am aware of the option to use class="affix" on the left column, b ...

Adding class names dynamically to div elements using jQuery can enhance the interactivity and styling of a webpage

I need to dynamically add a class name to multiple divs using jQuery. Specifically, I have 10 divs in mind. <div class="sec"></div> <div class="sec"></div> <div class="sec"></div> <div class="sec"></di ...

Utilizing the Public Directory in Vite Compilation

One issue I encountered in my project with Vite version 2.9.7 is related to the handling of images stored in the public folder within the project's root directory. To import these images, I utilized Vite's import.meta.glob function, like so: imp ...

Retrieve the initial identification number from a sorted list using Jquery UI Sortable

Situation: My task involves developing a mobile website that allows users to organize a list of names using the Jquery UI sortable option. Depending on the order in which the names are arranged, additional information will be displayed on the next page. ...

What is the advantage of utilizing the Q or Bluebird framework for handling promises?

While delving into the world of promises in node.js, a question arose. 'promise' is indeed already defined in node.js, yet additional promise frameworks like Q, bluebird, RSVP are commonly used. What could be the reason behind this? Could ...

I am constantly facing the same frustrating error in every meanstack project I work on: Failed to load resource: net::ERR_CONNECTION_REFUSED. Can't seem

I've been diving into educational resources to learn how to build meanstack applications with basic CRUD capabilities. After downloading various projects from Github and setting up the necessary modules, I encounter a consistent error when running the ...

Live streaming updates directly from Firebase

In order to achieve real-time updates from Firebase, my objective is to retrieve comments from Firebase and display them on this.note. I seem to have made a mistake in the update() function. Here is the tutorial I followed: link. methods: { update(){ db.c ...

Tips for querying multiple elements that share a common ID and verifying if the input has been modified

I have a series of text inputs that share a common id prefix but differ slightly at the end. Whenever a user enters text in any of these input fields, I want to trigger a function. Currently, I have this functionality implemented with the following code: ...

What is the best way to access and display the innerText of elements that have been removed using console

When I call the updateCartTotal() function, my goal is to display in the console the items that have been removed from the shopping cart. Every time I click on the remove button, I want it to show the item and the price. However, instead of displaying the ...

Angular and JS do not have the functionality to toggle the split button

I have a question that seems similar to others I've seen, but I haven't found a solution yet. Can someone please review my code? In the component, I have {{$ctrl.init}} and {{$ctrl.people}} assigned. I am sure this assignment is correct because ...

JavaScript does not reflect updates made to the ASP.Net session

After clicking the button, I trigger the JavaScript to retrieve the session information. However, I am encountering an issue where the value of the session is not being updated. alert('<%= Session["file"]%>'); ...

Using redux action in the onPaginationChange function instead of setPaginationState in the given example for the TanStack table - is there a way to

Provided this sample Is there a way to utilize by dispatching a redux action rather than using useState - setPaginationState? onPaginationChange: state => dispatch(browseItemModalActions.setPagination(state)) An error is appearing in the console: ...

Creating a carousel of cards using JavaScript, CSS, and HTML

Here is a visual reference of what I'm attempting to achieve: https://i.stack.imgur.com/EoQYV.png I've been working on creating a carousel with cards, but I'm struggling to synchronize the button indicators with card advancement when clicke ...

Ways to manage an element that has been loaded using the load query function

After implementing the query load function to update posts on the homepage, I was able to display the most up-to-date posts. However, a new issue arose: Whenever I updated all posts without refreshing the entire page, I needed a way to control the element ...

Extracting data from a targeted field in MongoDB: A step-by-step guide

Seeking the most effective method to extract specific field content in Mongo? Here's my Schema using Mongoose: module.exports = mongoose => { const Shop = mongoose.model( 'Shop', mongoose.Schema( { ...

Cannot proceed with module import: Type 'ModuleWithProviders<T>' must have a single type argument

ERROR in node_modules/@angular/fire/firestore/firestore.module.d.ts:7:74 - error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s). 7 static enablePersistence(persistenceSettings?: PersistenceSettings): ...

What is the best way to transfer the MODULE_OPTIONS_TOKEN from one Module to another Module?

In my quest to create a modular NestJS application, I have devised a structure consisting of 3 Modules – MainModule, Module A, and Module B. The MainModule imports ConfigModule and Module A. ConfigModule furnishes the necessary options for Module A, whic ...

AngularJS and Spring Rest team up for seamless drag-and-drop file uploads

I have successfully implemented file upload using AngularJS and Spring REST, but now I want to change it to use dropzone.js or any other drag and drop file upload method. I tried using the dropzone.js library, but I am facing issues integrating it with Ang ...

Combining Array Elements to Create a Unified Object with Vue

Is there a way to transform an array into a list of objects? Transform [ { "currenttime":43481.46983805556, "moped":"30 minutes", "car":"1 hour" } ] to { "currenttime":43481.46983805556, "moped":"30 minutes", ...