Reputation: 21
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>
Upvotes: 1
Views: 2840
Reputation: 193258
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:
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>
Upvotes: 3
Reputation: 100
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
Upvotes: 0
Reputation: 29521
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>
Upvotes: 2
Reputation: 34628
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
});
Upvotes: 2
Reputation: 134
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);
}
Upvotes: 0