nice ass
nice ass

Reputation: 16719

$.remove() is removing events from detached element?

The element is being appended to list items when mouse is over them, and detached when mouse is out.

When the element is clicked, it gets detached, and then the list item to which it was part of is removed from the DOM.

My HTML:

<div id="cart">
    <ul>
        <li> <a data-item="a">item A</a></li>
        <li> <a data-item="b">item B</a></li>
        <li> <a data-item="c">item C</a></li>
        <li> <a data-item="d">item D</a></li>            
        <li> <a data-item="e">item E</a></li>
        <li> <a data-item="f">item F</a></li>
        <li> <a data-item="g">item G</a></li>
        <li> <a data-item="h">item H</a></li>         
    </ul>    

</div>

And my JavaScript:

  function plugin(node, opts){
    var self = this;
        delKnob = $('<i />').text('×');

    this.cart = $(node);

    delKnob
      .on('click', function(){              
        var item = $(this).parent().find('[data-item]').data('item');
        $(this).detach();
        self.remove(item);
      });

    this.cart
      .on('mouseenter', 'li', function(){
        delKnob.appendTo(this);
      })
      .on('mouseleave', 'li', function(){
        delKnob.detach();
      });
  }

  plugin.prototype = {
    remove: function(item){
      var li = this.cart.find('[data-item="' + item + '"]').closest('li');
      li.addClass('removing');
      setTimeout(function(){
        li.remove();   
      }, 500);    
    }
  };

new plugin('#cart');

I've set up a fiddle example that resembles my code.

The problem is that the click listener from this element also gets removed with the list item, even though the element is being detached before $.remove() fires, so it shouldn't be affected.

What am I doing wrong here?

Upvotes: 3

Views: 233

Answers (2)

Jeff B
Jeff B

Reputation: 30099

You are correct in what you believe is going on, except that when you detach the delKnob, your mouse immediately enters the li which contained it, meaning it gets re-attached, so when the li is removed, delKnob is still a child and as such gets removed as well.

I tested this by adding a console.log() to the code:

this.cart.on('mouseenter', 'li', function () {
    console.log("delKnob attached!");
    delKnob.appendTo(this);
})

Fix this by removing the delKnob in the setTimeout:

setTimeout(function () {
    li.find('.delKnob').detach();
    li.remove();
}, 500);

This assumes adding the class delKnob to the element.

Demo: http://jsfiddle.net/AHjRN/

As a side note, I would simplify the whole thing and just add a delKnob to every li. Then you just show/hide them, and don't have to worry about removing the handler. It also simplifies the code quite a bit:

function plugin(node, opts) {
      this.cart = $(node);

      this.cart.find('li').append($('<i class="delKnob"/>').text('×').hide());

      this.cart.on('click', '.delKnob', function () {
          var li =  $(this).closest('li').addClass('removing');
          setTimeout(function () {
             li.remove();
          }, 500);
      });

      this.cart.on('mouseenter', 'li', function () {
          $(this).find('.delKnob').show();
      })
          .on('mouseleave', 'li', function () {
          $(this).find('.delKnob').hide();
      });   
};

Demo: http://jsfiddle.net/F3Qdp/

Upvotes: 6

kei
kei

Reputation: 20491

Instead of binding an event to the delKnob which is (re)moved around, you could use event delegation and bind an event to the container (this.cart) that handles any .delKnob descendant being clicked.

delKnob = $('<i class="delKnob" />').text('×');      

this.cart
  .on('click', '.delKnob', function(){              
    var item = $(this).parent().find('[data-item]').data('item');
    $(this).detach();
    self.remove(item);
  });

DEMO

Upvotes: 1

Related Questions