Javascript - How to Remove DOM elements using click events and classes?

12,969

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);        
   });
}
Share:
12,969
Frederick M. Rogers
Author by

Frederick M. Rogers

Updated on June 05, 2022

Comments

  • Frederick M. Rogers
    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
    Frederick M. Rogers about 8 years
    Thank 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
    Frederick M. Rogers about 8 years
    Addendum, article on event delegation is really helpful. Thank you!
  • Frederick M. Rogers
    Frederick M. Rogers about 8 years
    I can't find any reference to the "contains" method you are using on the classList object. Where does this method come from ?
  • Greg Burghardt
    Greg Burghardt about 8 years
    see Element.classList from the Mozilla Developer Network.
  • Frederick M. Rogers
    Frederick M. Rogers about 8 years
    Question, using your encapsulated method. What does the "child" argument reference ?
  • Luca Rainone
    Luca Rainone about 8 years
    you 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 in child var. This var is only inside the function context, so it will be not overwritten in the next cycle
  • Frederick M. Rogers
    Frederick M. Rogers about 8 years
    Found it, works a charm but i am bit worried about the browser comparability.
  • Greg Burghardt
    Greg Burghardt about 8 years
    You 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.