Reputation: 921
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.
Upvotes: 3
Views: 19094
Reputation: 18783
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.
Upvotes: 3
Reputation: 68383
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);
});
}
Upvotes: 1
Reputation: 16458
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.
Upvotes: 3