Javascript - How to Remove DOM elements using click events and classes?
Solution 1
This is a perfect case for event delegation. No need for jQuery at all:
(function(window, htmlElement) {
'use strict';
htmlElement.addEventListener("click", handleClick, false);
function handleClick(event) {
if (event.target.classList.contains("delete")) {
event.preventDefault();
removeItem(event.target);
}
}
function removeItem(button) {
var item = getItem(button),
confirmMessage;
if (item) {
confirmMessage = item.getAttribute("data-confirm");
if (!confirmMessage || window.confirm(confirmMessage)) {
item.parentNode.removeChild(item);
}
}
else {
throw new Error("No item found");
}
}
function getItem(button) {
var element = button.parentNode,
item = null;
while (element) {
if (element.nodeName === "LI" || element.nodeName === "TR") {
item = element;
break;
}
element = element.parentNode;
}
return item;
}
})(this, this.document.documentElement);
You have one click handler for the entire page, regardless of how many delete buttons you have. This should also work for list items or table rows, and by specifying a data-confirm
attribute on your buttons, it will pop up a confirm box before removing it.
<button type="button" class="delete"
data-confirm="Are you sure you want to delete this item?">
Delete
</button>
You can also easily change this so it uses another attribute to find the delete button:
<button type="button" class="delete"
data-delete
data-confirm="...">
Delete
</button>
Just change the condition of the if
statement in the handleClick function to:
if (event.target.hasAttribute("data-delete")) {
event.preventDefault();
removeItem(event.target);
}
This decouples your behavior from styling. You can use the delete
class name for styling, and the data-delete
attribute for JavaScript behavior. If you need to change the name of the CSS delete class for any reason, or use a different class, because you decide to use a third party CSS framework like Bootstrap, then you don't need to change a single line of JavaScript.
The last advantage here is that the click handler is attached to the document.documentElement
object, which is available the moment JavaScript begins executing and represents the <html>
element. No need for jQuery's document ready event handler. Just attach the click handler and import the script at any point on the page.
Solution 2
The problem is that your childClass[i]
that you call when you click an element, is not what you expect when you define the function.
You should use event.target
for catch the element clicked
var childElements = document.getElementsByClassName('item');
var buttonElement = document.getElementsByClassName('delete');
var _handler = function(e) {
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
}
function deleteItem(buttonsClass, childClass) {
for (var i=0;i<buttonsClass.length;i++) {
buttonsClass[i].addEventListener('click', _handler, false);
}
}
deleteItem(buttonElement, childElements);
-- edit --
If you want to use the original approach, then you can solve it in this way:
function deleteItem(buttonsClass, childClass) {
for (var i=0;i<buttonsClass.length;i++) {
(function(child) {
buttonsClass[i].addEventListener('click', function(e) {
child.parentNode.removeChild(child);
}, false);
})(childClass[i]);
}
}
With encapsulation (function(encapsulatedChild) { })(child)
you can store the value of child in a context that does not change during the next cycle.
Solution 3
Looks like you want to bind a click event to delete buttons, and on that event delete that item.
You need to fetch the child classes individual buttonsClass
elements.
function deleteItem( childElements )
{
Array.prototype.slice.call( childElements ).forEach( function( item ){
var deleteChildren = item.getElementsByClassName( "delete" );
Array.prototype.slice.call( deleteChildren ).forEach( function( deleteBtn ){
deleteBtn.addEventListener('click', function()
{
this.parentNode.removeChild( this );
}, false);
});
});
}
or even more simply, just pass the list of buttons on clicking which parent item
will be deleted
function deleteItem( buttonElement )
{
Array.prototype.slice.call( buttonElement ).forEach( function( button ){
button.addEventListener('click', function()
{
this.parentNode.removeChild( this );
}, false);
});
}
Frederick M. Rogers
Updated on June 05, 2022Comments
-
Frederick M. Rogers almost 2 years
I am having some difficulty using
parentNode.removeChild()
. I have a list of 'items' in an un-ordered list, each have there own delete button. I am trying bind a click event to each individual button that will delete it's respective parent 'item'.My code so far:
<ul class="list"> <h2>This is a list</h2> <li class="item"> <h3>Some Item</h3> <button class="delete">Delete</div> </li> <li class="item"> <h3>Some Item</h3> <button class="delete">Delete</div> </li> <li class="item"> <h3>Some Item</h3> <button class="delete">Delete</div> </li> </ul> var childElements = document.getElementsByClassName('item'); var buttonElement = document.getElementsByClassName('delete'); function deleteItem(buttonsClass, childClass) { for (var i=0;i<buttonsClass.length;i++) { var child = childClass[i]; buttonsClass[i].addEventListener('click', function(child) { childClass[i].parentNode.removeChild(childClass[i]); }, false); } } deleteItem(buttonElement, childElements);
I know there is an easier way to do this with jQuery but i really want to solve this with plain javascript. Thank you for any and all help.
-
Frederick M. Rogers about 8 yearsThank you, this ia a very elegant solution and does exactly what i want. The complexity is a bit above my skill level but now that i know it works i can dissect it components parts until i understand what it does. then i will try it as a object method.
-
Frederick M. Rogers about 8 yearsAddendum, article on event delegation is really helpful. Thank you!
-
Frederick M. Rogers about 8 yearsI can't find any reference to the "contains" method you are using on the classList object. Where does this method come from ?
-
Greg Burghardt about 8 yearssee Element.classList from the Mozilla Developer Network.
-
Frederick M. Rogers about 8 yearsQuestion, using your encapsulated method. What does the "child" argument reference ?
-
Luca Rainone about 8 yearsyou create a function with
(function(arg) { ... })
then execute it immediately passing the parameter that you want(childClass[i])
. In this way your childClass[i] reference is copied inchild
var. This var is only inside the function context, so it will be not overwritten in the next cycle -
Frederick M. Rogers about 8 yearsFound it, works a charm but i am bit worried about the browser comparability.
-
Greg Burghardt about 8 yearsYou Might Not Need jQuery is a useful resource for determining browser compatibility for many of the new HTML5/CSS3 features, including the JavaScript API's.