Guide on removing an item from a list using a JavaScript button

I am in the process of creating a basic task list that allows users to input tasks. When the add button is clicked, the task will be added to an unordered list along with a delete button. As a beginner in JavaScript, I am struggling to figure out how to make the delete button remove the specific list element when clicked (it's a span element, not a button).

Although I believe removeChild() could be useful for this task, I am having difficulty implementing it.


let addButton = document.getElementById("add-item");

addButton.addEventListener("click", function () {
    
    // Get the 'list'
    let list = document.getElementById("items");
    let textNode = window.prompt("Enter item:");
    if (textNode != null) {
        let item = document.createElement("li");
        // Convert user input into a textnode
        item.appendChild(document.createTextNode(textNode));
        // Append user's textnode at the end of the list
        list.appendChild(item);
        // Create a delete button 
        let deleteButton = document.createElement("span");
        deleteButton.innerHTML = "Delete"
        item.appendChild(deleteButton)
    }
});
ul {
    padding: 0px;
}

li {
    display: flex;
    background-color: #eee;
    margin: 5px;
    padding: 5px;
    align-items: center;
}

li > span {
    margin-left: auto;
    background-color: #aaa;
    padding: 5px;
    cursor: pointer;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <link href="app_2.css" rel="stylesheet>
    <title>Items</title>
</head>

<body>
    <h1>Items</h1>

    <ul id="items">
       <!--
            This is the template for an item in the list:

            <li>The first item is free! <span>Delete</span></li>
        -->
    </ul>
    
    <button type="button" id="add-item">Add item</button>

    <script src="app_2.js"></script>
</body>

</html>

Answer №1

You could implement a click event on the deleteButton in order to handle that scenario

deleteButton.addEventListener("click", function() {
   item.remove()
})

Here is the complete code snippet

let addButton = document.getElementById("add-item");

const currentIndex = 0

addButton.addEventListener("click", function() {

  // Accesses the 'list'
  let list = document.getElementById("items");
  let textNode = window.prompt("Enter item:");
  if (textNode != null) {
    let item = document.createElement("li");
    // Turns user input into textnode
    item.appendChild(document.createTextNode(textNode));
    // Appends user's textnode at the end of the list
    list.appendChild(item);
    // Generates a delete button 
    let deleteButton = document.createElement("span");
    // Attaches a click event to remove the current item
    deleteButton.addEventListener("click", function() {
      item.remove()
    })
    deleteButton.innerHTML = "Delete"
    item.appendChild(deleteButton)
  }
});
ul {
  padding: 0px;
}

li {
  display: flex;
  background-color: #eee;
  margin: 5px;
  padding: 5px;
  align-items: center;
}

li>span {
  margin-left: auto;
  background-color: #aaa;
  padding: 5px;
  cursor: pointer;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link href="app_2.css" rel="stylesheet">
  <title>Items</title>
</head>

<body>
  <h1>Items</h1>

  <ul id="items">
    <!--
            This is the template for an item in the list:

            <li>The first item is free! <span>Delete</span></li>
        -->
  </ul>

  <button type="button" id="add-item">Add item</button>

  <script src="app_2.js"></script>
</body>

</html>

Answer №2

One way to implement an event listener for the delete button and make it remove items from a list is shown below. Commentary has been added within the code to highlight the modifications made and provide an explanation of the functionality.

let addButton = document.getElementById("add-item");

addButton.addEventListener("click", function () {
    
    // Get the 'list' element
    let list = document.getElementById("items");
    let textNode = window.prompt("Enter item:");
    if (textNode != null) {
        let item = document.createElement("li");
        // Create a textnode using user input
        item.appendChild(document.createTextNode(textNode));
        // Append the user's textnode at the end of the list
        list.appendChild(item);
        // Create a delete button 
        let deleteButton = document.createElement("span");
        deleteButton.innerHTML = "Delete"
        item.appendChild(deleteButton);
        
        
        // Add an event listener for the delete button
        deleteButton.addEventListener("click", function(){
          
          // Get the parent of the span (li) 
          let listItem = this.parentNode;
          // Get the parent of the list item (ul)
          let list = listItem.parentNode;
          // Remove the child from the list         
          list.removeChild(listItem);
         
        });
    }
});
ul {
    padding: 0px;
}

li {
    display: flex;
    background-color: #eee;
    margin: 5px;
    padding: 5px;
    align-items: center;
}

li > span {
    margin-left: auto;
    background-color: #aaa;
    padding: 5px;
    cursor: pointer;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <link href="app_2.css" rel="stylesheet">
    <title>Items</title>
</head>

<body>
    <h1>Items</h1>

    <ul id="items">
        <!--
            This is the template for an item in the list:

            <li>The first item is free! <span>Delete</span></li>
        -->
    </ul>
    
    <button type="button" id="add-item">Add item</button>

    <script src="app_2.js"></script>
</body>

</html>

Answer №3

Implement event delegation. Instead of individually attaching event listeners to each button, attach a single listener to the parent element (in this case the ul element), which will handle all events from its child elements as they propagate up the DOM and then call a function to update the list.

I have chosen a more modern approach for constructing the rows in my response, which you might find helpful. References to relevant MDN documentation can be found at the end of the answer.

const items = document.querySelector('#items');
const addButton = document.querySelector('#add-item');

// Add event listeners to both the button and the items list element
// In the case of `addItem`, I'm directly invoking the function instead of just returning a reference to the function.
items.addEventListener('click', handleClick, false);
addButton.addEventListener('click', addItem(), false);

function handleClick(e) {

  // Since we are using event delegation, we need to validate that the clicked element is the button with the delete class
  if (e.target.matches('.delete')) {

    // If it is, extract the id from the element's dataset
    const { id } = e.target.dataset;

    // Use the id to create a selector
    const selector = `li[data-id="${id}"]`;

    // Remove the corresponding list item from the list
    items.querySelector(selector).remove();

  }

}

// To minimize global variables, I am using a closure where the inner function retains a copy of `id` which can be updated
function addItem() {

  let id = 0;

  return function () {

    const text = window.prompt('Enter item:');

    if (text) {

      // Using template strings to build the HTML instead of `createElement`
      // Adding the id to both the item and the button for proper deletion handling
      const html = `
        <li data-id="${id}">
          ${text}
          <button
            type="button"
            data-id="${id}"
            class="delete"
          >Delete
          </button>
        </li>
      `;

      // Inserting the HTML into the list
      items.insertAdjacentHTML('beforeend', html);
      
      // Incrementing the row id
      ++id;

    }

  }

}
ul{padding:0}
li{display:flex;background-color:#eee;margin:5px;padding:5px;align-items:center}
.delete{margin-left:auto;background-color:#aaa;padding:5px;cursor:pointer}
<h1>Items</h1>
<ul id="items"></ul>
<button type="button" id="add-item">Add item</button>

Additional resources

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

Monitor fetch() API calls and responses in JavaScript

I’m looking to intercept fetch API requests and responses using JavaScript. Specifically, I want to be able to capture the request URL before it is sent and also intercept the response once it has been received. The code below demonstrates how to inter ...

Switch between multiple unordered lists (ul) so that when one list is clicked, the other lists reset to their initial state as though they were never

When I click on the first item in the ul list, it should slideToggle() to show its corresponding items. Similarly, when I click on the second item in the ul list, its items should slideToggle(), but the first ul list remains visible as well. What I am tryi ...

What is the best way to click on the third div class "link" using Selenium when they all have the same div class?

While working with selenium and python, I encountered a scenario where all the HTML elements have the same class "links". There are seven links on the page, each sharing the same class and similar name. I am seeking guidance on how to interact with these w ...

What is the best method for securely storing and managing refresh and access tokens within a node.js application?

Currently, I am working with next.js and I am looking for a way to persist an API refresh token without using a database in my application. What would be the recommended practice for storing this token securely so that it can be updated as needed? Storin ...

Is there a solution for the continuous automatic incrementing of the jQuery UI spinner that occurs when I right-click on it?

Only Linux and Mac OS users are facing this particular issue, indicating a potential bug with the jQuery spinner. The bug also affects the spinner located at the following link: https://jqueryui.com/spinner/ <input class="spinner"/> $(".spinner"). ...

Why isn't the VueJS component loading state getting updated after Canceling an Axios network request?

Within my dashboard, there is a dropdown for filtering dates. Each time a user changes the dropdown value, multiple network requests are sent using Axios. To prevent additional API calls when the user rapidly changes the date filters, I utilize AbortContr ...

Trigger the function when the keyboard event is deactivated

Is there a way to continuously run the top set interval whenever I lift my finger from the space key? When I try using the key up event, it only executes that function once. I'm not sure how to implement if/else logic when adding an event listener. se ...

Issue with JQuery's parentsUntil method when using an element as a variable not producing the desired results

I'm having trouble with a specific coding issue that can be best illustrated through examples: For example, this code snippet works as expected: $(startContainer).parents().each(function(index, parentNode) { if (parentNode.isSameNode(commonConta ...

Unable to properly cancel a post request using abort functionality

In the process of building a Next.js app, I encountered an issue with calling a post request. I included a cancel button to halt the post request, and attempted to use abortController in conjunction with Axios (v1.4.0) to achieve this. Even though the &ap ...

The NPM version needs to be updated as it is outdated

Struggling with a dilemma here. My Laravel project is quite dated, and I'm unable to execute npm run dev. Let's take a look at some code: php artisan laravel --version: Laravel Framework 5.8.38 node --version: v16.16.0 This is the current Node v ...

Troubleshooting spacing and padding problems in Bootstrap 5.2.3's Scrollspy feature

Hello, I'm currently working on developing a new portfolio template that features a list on the left side to guide users through different sections of the page. Utilizing Scrollspy in Bootstrap 5.2.3 has been helpful so far, but I've noticed that ...

Unable to retrieve the 'href' on different pages

Why am I unable to retrieve the "href" from other pages, but still can on the first page? Is there something wrong with it? Will changing the Xpath allow me to get all "href"s from every page? !pip install selenium from selenium import webdriver import t ...

Could someone explain to me why searchQuery.value is coming up as undefined?

Within my development task, I am endeavoring to create a functional search icon on the header bar that allows users to input Pokémon names for searching purposes. Despite my efforts, I am consistently facing a console error message suggesting "searchQu ...

Display error page when unable to load ng-view template

Is there a way to display a standard error page or template if a certain template cannot be loaded using ngRoute in Angular? I've come across suggestions that subscribing to the $routeChangeError event might help, but I'm not sure what steps to ...

The Ion-button seems to be malfunctioning

I am interested in using special buttons for my ionic 1 project, specifically the ion-button feature outlined on this page: Ionic Buttons. I attempted to create a Round Button and an Outline + Round Button: <h2 class="sub-header" style="color:#4 ...

the div background is limited to the exact size of the text, not filling the entire

Currently, as I work on my web page using react.js, I'm facing the challenge of implementing a full-size background. Despite my efforts, the background only occupies the size of the text within the div. Here is the snippet of code I am working with: a ...

Using Vue Js directive to implement a Select2 component

I've been exploring the example of the Vue.js wrapper component and trying to customize it to use a v-select2 directive on a standard select box, rather than creating templates or components for each one. You can view my implementation in this JS Bin ...

Troubleshooting the Ineffectiveness of the CSS Custom Property Url in Asp .Net

Working on styling some cards within an MVC project, I have a custom property in my CSS file called "--bg-img" which is defined as follows: background-image: linear-gradient(rgba(0, 0, 0, var(--bg-filter-opacity)), rgba(0, 0, 0, var(--bg-filter-opacity))) ...

Making a column in a Vue data grid return as a clickable button

My goal is to utilize vue.js grid to display multiple columns with calculated text values, along with a clickable column at the end that triggers a dynamic action based on a parameter (such as calling an API in Laravel). However, when I include the last c ...

Encountering an issue with Apollo Express GraphQL: Error message stating that the schema must have distinct type names, yet it contains more than one type named "DateTime"

After importing the applyMiddleware library from 'graphql-middleware' to add validation middleware on mutation's input, I created a sample middleware function that logs the input. export const logInput = async (resolve, root, args, context, ...