简体   繁体   中英

JS delete specific <li> element by addEventListener()

I am a newbie in JS and DOM.

I'd like to delete a specific li item by clicking on its child delete button by using addEventListener(). I have found a few relevant solutions like this one , but I'm trying to come up with a more elegant solution.

<ul>
    <li>Notebook</li>
    <li>Jello</li>
    <li>Spinach</li>
    <li>Rice</li>
    <li>Birthday Cake</li>
    <li>Candles</li>
</ul>

The following is a snipped JavaScript code I have tested but not had success with.

function removeItem(e) {
    e.target.parentNode.remove();
}

deleteButton.addEventListener("click", removeItem);

Here is the full code:

 var button = document.getElementById("enter"); var input = document.getElementById("userinput"); var ul = document.querySelector("ul"); var deleteButton = document.createElement("button"); Array.from(ul.children).forEach( function(item) { appendDeleteBtnTo(item); }) function appendDeleteBtnTo(element) { var deleteButton = document.createElement("button"); deleteButton.innerHTML = "Delete"; deleteButton.classList.add("delete"); element.appendChild(deleteButton); } function lineThrough(e) { if (e.target.tagName === "LI") { e.target.classList.toggle("done"); } } // =========== not working ==================================== function removeItem(e) { e.target.parentNode.parentNode.remove(); } deleteButton.addEventListener("click", removeItem); // ============================================================= function inputLength() { return input.value.length; } function createListElement() { var li = document.createElement("li"); li.appendChild(document.createTextNode(input.value)); ul.appendChild(li); input.value = ""; appendDeleteBtnTo(li); } function addListAfterClick() { if (inputLength() > 0) { createListElement(); } } function addListAfterKeypress(event) { if (inputLength() > 0 && event.keyCode === 13) { createListElement(); } } ul.addEventListener("click", lineThrough); button.addEventListener("click", addListAfterClick); input.addEventListener("keypress", addListAfterKeypress);
 .done { text-decoration: line-through; } ul li:hover { background-color: rgba(0, 0, 0, 0.05); }
 <!DOCTYPE html> <html> <head> <title>Javascript + DOM</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <h1>Shopping List</h1> <p id="first">Get it done today</p> <input id="userinput" type="text" placeholder="enter items"> <button id="enter">Enter</button> <ul> <li class="bold red" random="23">Notebook</li> <li>Jello</li> <li>Spinach</li> <li>Rice</li> <li>Birthday Cake</li> <li>Candles</li> </ul> <script type="text/javascript" src="script.js"></script> </body> </html>

Use event delegation by setting the event handler on the parent, and checking who is the actual target in the handler. This has two advantages:

  1. You can add/remove list items without the need to attach event handlers to the added item.
  2. You can easily combine the delete and line through functionalities in a single simple handler.

The event handler is added to the list ( ul ). Whenever the list or its children are click, the handler checks if the target is/inside a list item use Element.closest() . If no list item is found in the chain, the function terminates ( return ). If a list item is found ( target ), we check if the actual click target was the delete button. If it's the delete button the target is removed. If not, the the done class is added.

function listItemClickHandler(e) {
  const li = e.target.closest('li');

  if(!li) return;

  if (e.target.matches('.delete')) {
    li.remove();
  } else {
    li.classList.toggle('done');
  }    
}

 var button = document.getElementById("enter"); var input = document.getElementById("userinput"); var ul = document.querySelector("ul"); var deleteButton = document.createElement("button"); Array.from(ul.children).forEach(function(item) { appendDeleteBtnTo(item); }) function appendDeleteBtnTo(element) { var deleteButton = document.createElement("button"); deleteButton.innerHTML = "Delete"; deleteButton.classList.add("delete"); element.appendChild(deleteButton); } function inputLength() { return input.value.length; } function createListElement() { var li = document.createElement("li"); li.appendChild(document.createTextNode(input.value)); ul.appendChild(li); input.value = ""; appendDeleteBtnTo(li); } function addListAfterClick() { if (inputLength() > 0) { createListElement(); } } function addListAfterKeypress(event) { if (inputLength() > 0 && event.keyCode === 13) { createListElement(); } } function listItemClickHandler(e) { const li = e.target.closest('li'); if(!li) return; if (e.target.matches('.delete')) { li.remove(); } else { li.classList.toggle('done'); } } ul.addEventListener("click", listItemClickHandler); button.addEventListener("click", addListAfterClick); input.addEventListener("keypress", addListAfterKeypress);
 .done { text-decoration: line-through; } ul li:hover { background-color: rgba(0, 0, 0, 0.05); }
 <h1>Shopping List</h1> <p id="first">Get it done today</p> <input id="userinput" type="text" placeholder="enter items"> <button id="enter">Enter</button> <ul> <li class="bold red" random="23">Notebook</li> <li>Jello</li> <li>Spinach</li> <li>Rice</li> <li>Birthday Cake</li> <li>Candles</li> </ul>

I'm trying to come up with a more elegant solution

Since your callback is neither long nor elaborate, you can - rather than calling a named function - add an anonymous callback to your .addEventListener() method which contains the ES2015 .remove() :

.addEventListener('click', (e) => e.target.parentNode.remove(), false)

Working Example:

 const shoppingList = document.getElementsByTagName('ul')[0]; const shoppingListItems = [... shoppingList.getElementsByTagName('li')]; for (shoppingListItem of shoppingListItems) { let deleteButton = document.createElement('button'); deleteButton.type = 'button'; deleteButton.classList.add('deleteButton'); deleteButton.textContent = 'Delete this'; deleteButton.addEventListener('click', (e) => e.target.parentNode.remove(), false); shoppingListItem.appendChild(deleteButton); }
 li { padding: 6px 0; font-size: 16px; line-height: 24px; } .deleteButton { display: inline-block; margin-left: 12px; }
 <ul> <li>Jello</li> <li>Spinach</li> <li>Rice</li> <li>Birthday Cake</li> <li>Candles</li> </ul>

From your demo (truncated):

var deleteButton = document.createElement("button"); //<-- never inserted into DOM
//...
function appendDeleteBtnTo(element) {
    var deleteButton = document.createElement("button");
    deleteButton.innerHTML = "Delete";
    deleteButton.classList.add("delete");
    element.appendChild(deleteButton);
}
deleteButton.addEventListener("click", removeItem);

The problem is the deletion button you're assigning the event to is not the same as the deletion button you're ultimately appending to each li - that's being done quite separately inside appendDeleteBtnTo() . In fact, the one you're binding to is never actually inserted into the DOM.

You could bind the event inside that same function. Better, and more economical, would be to bind the event to the body (or at least some container) and thus 'delegate' the event, checking when it fires whether it applies to your specific circumstance.

We do that by checking what populated the target property of the event object.

document.body.addEventListener('click', evt => {
    if (!evt.target.matches('li > button')) return; //quit - click was not to li > button
    removeItem(evt); //remove li
});
Add Event Listener to each delete button where they have created.

function appendDeleteBtnTo(element) {
    var deleteButton = document.createElement("button");
    deleteButton.innerHTML = "Delete";
    deleteButton.classList.add("delete");
    deleteButton.addEventListener("click", removeItem); // add the event lisntener
    element.appendChild(deleteButton);
}

function removeItem(e) {
    e.target.parentNode.remove();
}

See the edited jsFiddle

Here We want to attach the event listener to each of the delete buttons that are being created. See the modified fiddle.

function appendDeleteBtnTo(element) {
    var deleteButton = document.createElement("button");
    deleteButton.innerHTML = "Delete";
    deleteButton.classList.add("delete");
    element.appendChild(deleteButton);

    deleteButton.addEventListener("click", removeItem);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM