Using JavaScript to listen for events on all dynamically created li elements

Recently, I've created a simple script that dynamically adds "li" elements to a "ul" and assigns them a specific class. However, I now want to modify the class of an "li" item when a click event occurs.

Here's the HTML structure:

<form class="form">
<input id="newInput" type="text" placeholder="Add item">
<button id="createNew" type="button">Add</button>
</form>
<h2>My List:</h2>
<div class="listBg">
<ul id="list">
</ul>
</div>
<button id="deleteAll" type="button">Clear All</button>

And here's the corresponding JavaScript code:

function addItem() {
    var myList = document.getElementById("list");
    var newListItem = document.createElement("li");
    var itemText = document.getElementById("newInput").value;
    var listText = document.createTextNode(itemText);
    newListItem.appendChild(listText);
    if (itemText === "") {
        alert("Field cannot be empty");
    } else {
        var x = document.createElement("span");
        x.innerText = "X";
        x.className = "closer";
        myList.appendChild(newListItem);
        newListItem.className = "item";
        newListItem.appendChild(x);
        var itemText = document.getElementById("newInput");
        itemText.value = "";
    }
};

function itemDone() {
    var listItems = document.querySelectorAll("li");
    for (var i = 0; i < listItems.length; i++) {
        listItems[i].classList.add("itemDone");
    }
};

var items = document.getElementsByClassName("item");
for (var j = 0; j < items.length; j++) {
    items[j].addEventListener("click", itemDone);
}

I'm still learning JavaScript, so any additional tips or explanations would be greatly appreciated!

Answer №1

Utilize event delegation for handling dynamically created elements. By implementing this technique, you can have just one event listener on the ul#list that will efficiently handle all elements dynamically added to it:

document.getElementById("list").addEventListener("click", function(e) {
  if (e.target && e.target.matches("li.item")) {
    e.target.className = "foo"; // specify new class name here
  }
});

Here's a simplified example to demonstrate how the code works:

function addItem(i) {
  var li = document.createElement('li');
  li.appendChild(document.createTextNode(i));
  li.className = 'item';
  document.getElementById('list').appendChild(li);
}

var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
  addItem(counter++);
});

document.getElementById("list").addEventListener("click", function(e) {
  if (e.target && e.target.matches("li.item")) {
    e.target.className = "foo"; // specify new class name here
    alert("clicked " + e.target.innerText);
  }
});
<ul id="list">
  <li class="item">1</li>
</ul>

<button id="btn">
  add item
</button>

Answer №2

In order to make sure each item has an event listener, you will need to set the eventListener on each individual item. This is because

document.getElementsByClassName()
returns a collection of items, and you cannot add an event listener to all of them at once using addEventListener().

Similar to how you looped through the items in the itemDone() function, you will need to iterate over each item and attach the event listener to it:

var items = document.getElementsByClassName("item");
for (var i = 0; i < items.length; i++) {
  items[i].addEventListener("click", itemDone);
}

Alternatively, as mentioned in the comments, you can directly add the event listener when creating the elements in your addItem() function by including the following line:

newListItem.addEventListener("click", itemDone);

Answer №3

Give this a shot:

let items = document.querySelectorAll(".item");
items.forEach(item => {
  item.addEventListener("click", updateItem);
});

Answer №4

function createNewItem(item) {
  var newItem = document.createElement('li');
  newItem.appendChild(document.createTextNode(item));
  newItem.className = 'item';
  document.getElementById('list').appendChild(newItem);
}

var counter = 2;
document.getElementById('btn').addEventListener('click', function() {
  createNewItem(counter++);
});

document.getElementById("list").addEventListener("click", function(event) {
  if (event.target && event.target.matches("li.item")) {
    event.target.className = "selected"; // new class name to indicate selection
    alert("You clicked: " + event.target.innerText);
  }
});
<ul id="list">
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
  <li class="item">1</li>
</ul>

<button id="btn">
  Add New Item
</button>

Answer №5

When looking to optimize your code, consider using getElementByTagName in place of querySelectorAll as it is known to be faster. Also don't forget that item receives an array, so calling addEventListener directly on it will result in an error. You need to loop through the items and add the event listener to each item separately.

Answer №6

When the element with ID "list" is clicked, this function gets called. It finds the parent of the clicked element and then looks for all siblings that are list items (LI). For each sibling, it checks if it is the same as the clicked element. If so, it adds a class "foo", otherwise it removes any existing classes.

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

Interested in leveraging string functions on the information retrieved from the API?

I am trying to utilize String functions such as slice(x,y), length() on the data received from the API. To achieve this, I first converted the data to a variable called mystr using JSON.stringify(obj), but encountered an issue where the console displayed: ...

What significance does comparing two arrays hold in the realm of Javascript?

While working in my node.js REPL, I have created 4 arrays: a = [1,2,3], b=[], c=[4,5], d=null (although d is not actually an array). I decided to compare them directly like this: > b = [] [] > a > b true > b > a false > a > c false & ...

Innovative react route

Currently, I am in the process of learning about dynamic react routes. In the code example I am working on, there are different buttons for each task. The goal is to render the WorkDetails component when a button is clicked. However, it seems to not be fun ...

The error message "ECONNRESET" occurred while attempting to send a post request using Axios to

Attempting to send a post request to my webserver using axios, I have a client that collects user input to populate an array of strings. This data is then sent via a post request using axios for processing by the server: if (parsedInput > 0 &&am ...

Is there a method to ensure equal alignment of the <div class="card"> elements in Bootstrap?

I need help figuring out how to align this card uniformly with the others: https://i.sstatic.net/x2wZ2.png In the image below, you can see that the card for the top reason for downtime doesn't match the other cards. I want them all to be the same he ...

Is there a way for me to incorporate a feature where the user has the option to choose the number of passwords they would

I created a password generator for my company and now I want to implement the feature where the user can choose how many passwords they want. My attempt at using str_repeat ($output , $password_amount ) resulted in duplicate passwords being generated, mak ...

Is it possible to expand the CORS permissions to the routers directly from the app?

I have a couple of questions: Is it possible to just use cors() once in my server.js instead of having to call and use it in every router file? Can I simply require express once in my server.js without having to call it in all my router files? Currently, ...

A guide on how to retrieve POST form fields in Express

My form design is quite simple: <form id="loginformA" action="userlogin" method="post"> <div> <label for="email">Email: </label> <input type="text" id="email" name="email"></input> </div> & ...

"Creating a function within a knockout viewmodel that is populated with JSON data: A step-by-step guide

Struggling with defining a function inside my viewmodel. I retrieve json data using jquery getJSON and then map it to the viewmodel. $.getJSON('/Company/GetCompanies', function(data) { var viewModel = new CompanyViewModel() viewModel.m ...

Triggering jQuery events can be customized by excluding certain elements using the

Is there a way to hide the div "popu" when clicking on the img "tri"? I've tried using .not() since the img is a child of the div popu, but it didn't work. Also, I need to make sure that clicking on the div "textb" does not trigger the hide actio ...

Ensure that the jQuery ajax function triggers only after the images or iframes have completely loaded

I am currently in the process of creating an online portfolio. My goal is to have project information load into the current page via ajax when a user clicks on a specific project. However, I am facing an issue with the timing of the load() success function ...

HTML Remover resulting in a malfunction

Currently in the process of removing HTML tags from text using the following method: <p><b>Masala</b> films of <a href="/wiki/Cinema_of_India" title="Cinema of India">Indian cinema</a> are those that combine different genres ...

Personalized dropdown filters for Bootstrap Datatable

In my codeigniter project, I am utilizing Bootstrap datatables. In the footer section, I have included the datatables js and initialized it like so: $('.datatable').dataTable({ "sDom": "<'row-fluid'<'span6'l ...

Guide on extracting the text from the <script> tag using python

I'm attempting to extract the script element content from a generic website using Selenium. <script>...</script> . url = 'https://unminify.com/' browser.get(url) elements = browser.find_elements_by_xpath('/html/body/script[ ...

Issue: Angular 14 - Validators Not Resetting in Nested FormGroup

I am currently working on implementing a nested FormGroup. However, I have encountered an error when attempting to reset the form. Here is the structure of the form: form: UntypedFormGroup; this.form = this.fb.nonNullable.group({ f1: [''], f2: ...

Intercommunication of variables among components

My scenario involves two components, namely App and SomeComponent. I'm aiming to access a variable in App from SomeComponent. App: import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css& ...

Image not yet clicked on the first try

I am encountering an issue with my image gallery. Currently, when I click on a thumbnail, the large image is displayed. However, I would like the first image to show up without requiring the user to click on its thumbnail. How can I address this problem? B ...

Preserving the selected options in a dynamically populated dropdown list after submitting a form using php

I am attempting to preserve form values even after submitting the form. document.getElementById('start_date').value = "<?php echo $_POST['start_date'];?>"; document.getElementById('end_date').value = "<?php echo $_P ...

Leveraging jQuery for Crafting a Quiz with True or False Questions

Exploring the most effective approach to constructing a questionnaire. Find images below for reference. The current code setup is functional but becomes lengthy after just two questions. How can I streamline this code to minimize repetition? // prevent d ...

Floating elements can be vertically aligned with spans

I am facing an issue with aligning three spans vertically inside a div. It seems simple to achieve, but vertical alignment is not working properly when I use float. My goal is to have the light blue bar vertically centered. Here is the code snippet: .co ...