Adding a remove item function that is inserted with innerHTML? - javascript

I have an add to cart functionality and I am trying to create a remove from cart button. The problem I am having is getting the remove button to be unique to each cart item. It is currently functioning very funny, clicking the first item in the cart deletes all of the other items, and if I select a cart item other than the first one it deletes a different one. How can I make this work? I want to remove the entire LI element upon click.
let gridItems = document.querySelectorAll(".grid-item");
let ul = document.querySelector(".footer-container");
let num = 0;
gridItems.forEach((item) => {
const button = item.querySelector(".add-btn");
button.addEventListener("click", () => {
ul.insertAdjacentHTML(
"beforeend",
`<li class="footer-items">
<button class="remove-item">X</button>
<div class="side-by-side">
${
item.querySelector(".title").textContent
} <img class="cart-image" src="${item
.querySelector(".bike-image")
.getAttribute("src")}"/></div> <span class="cart-price">${
item.querySelector(".item-price").textContent
}</span> </li>`
);
//My attempt at removing each item
document
.querySelector(".remove-item")
.addEventListener("click", function () {
document.querySelector(".footer-items").remove();
});
});
});

I was able to figure out a solution to the problem by creating a for each loop and targeting the parentNode selector for each item and removing it.
document.querySelectorAll(".remove-item").forEach((item) => {
item.addEventListener("click", function () {
item.parentNode.remove();
});
});

Related

How to make the event listener only add CSS element on one item at a time (vanilla JavaScript)

^I would like to be able for the style to be enabled for only one at a time.
^I'm able to do this, which I don't want the user to be able to do.
So it's weirdly hard framing a question for what is possibly an easy solution. I basically have a list of build versions where I want the user to select one. When one of the versions are selected, it adds a border to the item to display that its clicked. However, with my code right now the user is able to select all 3 items and enable their CSS elements. I would like for the user to be able to only "activate" one item from the list.
HTML and CSS:
<ul class="listContents">
<li><p>Stable</p></li>
<li><p>Preview</p></li>
<li><p>LTS</p></li>
</ul>
<style>
.colorText {
background-color: #58a7ed;
color: white;
}
</style>
and the JS stuff:
const btn = document.querySelectorAll('.links');
for (let i = 0; i < btn.length; i++ ) {
btn[i].addEventListener("click", function() {
btn[i].classList.add('colorText')
})
}
I really hope I made myself clear, I feel like I'm failing my English trying to word this right lol.
You can also use a forEach loop, accessing the clicked link using event.target
const btns = document.querySelectorAll('.links');
btns.forEach(btn => {
btn.addEventListener('click', e => {
// remove any existing active links
btns.forEach(b => b.classList.remove('colorText'));
// activate the clicked link
e.target.classList.add('colorText');
})
});
.colorText {
background-color: #58a7ed;
color: white;
}
<ul class="listContents">
<li>
<p>Stable</p>
</li>
<li>
<p>Preview</p>
</li>
<li>
<p>LTS</p>
</li>
</ul>
Just before you add the colorText class to the desired item, we can remove colorText from ALL of them, ensuring that only 1 at a time gets the class.
// the rest is the same...
btn[i].addEventListener("click", function() {
// remove it from all:
btn.forEach(function(item) {
item.classList.remove('colorText');
});
// add it back to the desired one
btn[i].classList.add('colorText')
})
you can also use simple for of
const btn = document.querySelectorAll(".links");
for (let bt of btn) {
bt.addEventListener("click", (e) => {
btn.forEach((b) => b.classList.remove("colorText"));
e.target.classList.add("colorText");
});
}

I want to delete items from dynamic div using a delete button inside it

// Create a row for the item in view
const order_item = document.createElement('div');
order_item.className = `ordered_item`;
order_item.innerHTML = `
<div class="product-name">${item.namn}</div>
<div class="product-percentage">${item.alkoholhalt}</div>
<div class="product-price">${item.prisinklmoms}</div>
<button class="btn-selected" onclick="delOrderlist()">Del</button>
`;
orderContainer.appendChild(order_item);
This item is created dynamically and a button is there inside it. I want to delete the itemlist using this button.
There is possibly some confusion between dealing with a list of orders and a single order.
Here is a snippet which deals with a single order. When an order's button is clicked you can pass the function the element (this). You can then find the parent of this element and remove it. In the given code there seemed to be an attempt to go through deleting a whole list, which is not what the deletion of a single order requires.
const orderContainer = document.getElementById('orderContainer');
const order_item = document.createElement('div');
order_item.className = 'ordered_item';
order_item.innerHTML = '<div class="product-name">${item.namn}</div><div class="product-percentage">${item.alkoholhalt}</div><div class="product-price">${item.prisinklmoms}</div><button class="btn-selected" onclick="delOrder(this)">Del</button>';
orderContainer.appendChild(order_item);
const delOrder = (el) => {
el.parentElement.remove();
}
<div id="orderContainer"></div>

How can I delete an entire list item by clicking a button placed within it?

I'm making a to-do list application. I want to delete items by clicking a button attached to the list element, but it only deletes the button and not the entire element. Currently, <li> elements in a <ul> by the following:
function newElement() {
event.preventDefault(); // stop default redirect
var li = document.createElement("li"); // create an <li> element
/* make a text node from the input and put it in the <li> */
var inputValue = document.getElementById("task").value; // retrieve value of text box
var t = document.createTextNode(inputValue); // create a text node of the box value
li.appendChild(t); // put the text node in the single <li>
/* attach a button to the <li> element that deletes it */
var btn = document.createElement("BUTTON"); // Create a <button> element
btn.innerHTML = "X"; // Insert text
btn.className = "button" // add class name for CSS targeting
btn.addEventListener('click', removeItem); // add event listener for item deletion
li.appendChild(btn); // Append <button> to <li>
li.addEventListener('click', checkToggle); // add event listener for a click to toggle a strikethrough
appendItem("list", li); //append the li item to the ul
}
and the function called by the button's listener appears as:
function removeItem() {
this.parentNode.removeChild(this);
}
I want the button to delete the entire <li> node, but it only deletes the button portion of it.
Just looking at this, I think you need to add another parentNode. It seems you are only removing the button right now.
Just move this one step further up the hierarchy
function removeItem() {
this.parentNode.parentNode.removeChild(this.parentNode);
}
Use remove() so you are not dealing withe child element references
function removeItem() {
this.parentNode.remove()
// this.closest('li').remove()
}
or since you have a reference, just delete it
btn.addEventListener('click', function () { li.remove() })

How can i delete a specific li list on click?

const list = document.getElementById('list')
const addtodo = document.getElementById('addtodo')
//const items = document.querySelector('.item')
const deleteItem = document.querySelector('.delete')
addtodo.addEventListener('keypress', submitTodo)
function submitTodo(event) {
if (event.keyCode == 13) {
event.preventDefault()
let value = addtodo.value
let li = document.createElement('li')
li.innerHTML = `
<img class="unchecked" src="icon/unchecked.svg" />
${value}
<img class="delete" src="icon/icons8-multiply-26.png" /> `
list.appendChild(li)
}
}
deleteItem.addEventListener('click', items)
function items(item) {
if (item.target.classList.contains('delete')) {
item.target.parentElement.remove()
}
}
The code above only allows me to delete one item and its the first one on the list
I try to solve it on my own but couldn't any idea whats wrong
When deleteItem.addEventListener('click', items) is ran, it only attaches the eventListener to the elements currently on the DOM - the ones you create dynamically will not have this eventListener
You can use 'event delegation' instead to listen for clicks, and filter theses clicks based on if the click was coming from the correct element
You can read more about event delegation from davidwalsh's blog and this StackOverflow answer
document.addEventListener('click', function(e) => {
if(e.target && e.target.classList.includes('delete')){
e.target.parentElement.remove()
}
});
You could also make use of the elements onclick attribute, and pass this in the parameter of the function call - this way you can access the HTML Element directly from the parameter; this also avoids having to have an eventListener, or using an if to check if it's the correct class / ID
// Add the function to the onclick attribute of the img
<img class="delete" onclick="deleteItem(this)" src="demo.png" />
// in this function, 'item' refers to the DOM Element that was clicked
function deleteItem (item) {
item.parentElement.remove();
}
This code will allow you to remove the first element on ul .
let list = document.getElementById("list"); // ul element
let remove = document.getElementById("remove"); // remove button
// on double click event
remove.onclick = () => {
// remove the first child from the list
list.removeChild(list.firstChild);
}
<ul id="list">
<li>One</li>
<li>Two</li>
<li>three</li>
</ul>
<input type="button" id="remove" value="Remove First"></input>

cannot delete last item from todo list

I've made a simple todo list which so far has 2 functions. Checks off an item and deletes an item from a list.
I can delete all items if I start deleted from the bottom of the list but when I delete from the top, the first item gets deleted, the second item (now the first), deletes the item after it and then doesn't delete itself when I click on the X
I thought a stopPropagration() would help but doesn't seem to. It only stops the other checked function from running.
<ul>
<li><span>X</span> Code something</li>
<li><span>X</span> Wake up early</li>
<li><span>X</span> Buy popcorn</li>
</ul>
/**
* This strikes through list item on click once and item is marked as done
*/
const listItem = document.getElementsByTagName('li')
for (let i = 0; i < listItem.length; i++) {
listItem[i].addEventListener('click', () => {
listItem[i].classList.toggle('checked')
})
}
/**
* This deletes an item from the list on click
*/
const deleteItem = document.getElementsByTagName('span')
for (let i = 0; i < deleteItem.length; i++) {
deleteItem[i].addEventListener('click', e => {
deleteItem[i].parentNode.remove()
e.stopPropagation()
})
}
https://jsfiddle.net/k2u8mqes/
Expected result is that I should be able to delete each item, in any order
Your for loop is assigning some clicks to different elements from what you intend.
This suggestion uses the target of the click event to decide what needs to be deleted in real time. (Specifically, it removes the li that is the parent of the clicked span.)
document.addEventListener("click", checkLi);
document.addEventListener("click", deleteSpanParent);
function checkLi(event){
if(event.target.tagName == "LI"){
event.target.classList.toggle("checked");
event.target.style.color = "grey";
}
}
function deleteSpanParent(event){
if(event.target.tagName == "SPAN"){
let span = event.target, li = span.parentNode, ul = li.parentNode;
ul.removeChild(li);
}
}
<ul>
<li><span>X</span> Code something</li>
<li><span>X</span> Wake up early</li>
<li><span>X</span> Buy popcorn</li>
</ul>
You are modifying an array while iterating it, which is a common caveat.
When you delete from top:
deletes the 1st item, calls deleteItem[0].parentNode.remove(), removes the 1st element of deleteItem array, it's okay;
deletes the 2nd item, calls deleteItem[1].parentNode.remove(), but the deleteItem is now of size 2, you are actually deleting the
3rd element of the original array;
deletes the 3rd item, calls deleteItem[2].parentNode.remove(), but the deleteItem is now of size 1, you are running out of index;
Working snippet:
for (let i = 0; i < deleteItem.length; i++) {
deleteItem[i].addEventListener('click', e => {
e.target.parentNode.remove()
})
}
The problem is where the way you have used for loops to bind event listeners and how you're using accessing the particular element which the event bound to.
Instead of doing this;
listItem[i].classList.toggle('checked')
//and
deleteItem[i].parentNode.remove()
You can use the event listener's event object to access to bound element, like this;
e.target.classList.toggle('checked')
//and
e.target.parentNode.remove()
Please refer to the event listener API here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
check this fiddle for a working example of your code : https://jsfiddle.net/491rLbxj/

Categories