4

example fiddle

JavaScript

$('#btn').on('mouseover', function() {
    $('#tt').show();
}).on('mouseleave', function() {
    $('#tt').hide();
}).on('click', function() {
    $(this).remove();
});

HTML

<button id="btn">press me</button>
<div id="tt">tooltip</div>

Basically, when you remove an element while your cursor is still over it, the mouseleave event is never triggered. I guess this makes sense because if the element is gone, the event/binding is gone too.

But how do we work around it?

Sure, I could put $('#tt').hide(); in the click event too, but I was hoping for a more generic solution. My real-life example is a bit more complex and I don't always know when the element is going to be removed.

There's no ondestroy event or anything I can hook into, that will fire just before it's removed, is there?

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 1
    How about triggering a mouseleave event http://jsfiddle.net/Sjt2u/1/ – Musa Apr 17 '13 at 02:14
  • 1
    @Musa: That's pretty much identical to my point about adding something to the click handler... the element may be removed in various ways, I don't want to have to augment all of them. – mpen Apr 17 '13 at 03:11

1 Answers1

2

I've updated your jsFiddle with the code from this solution.

(function($){
  $.event.special.destroyed = {
    remove: function(o) {
      if (o.handler) {
        o.handler()
      }
    }
  }
})(jQuery)

$('#btn').on('mouseover', function() {
  $('#tt').show();
}).on('mouseleave', function() {
  $('#tt').hide();
}).on('click', function() {
  $(this).remove();
}).on('destroyed', function() {
  $('#tt').hide();
})

Alternatively, you can bind to the DOMNodeRemoved mutation event, but the W3 spec states that this event type is deprecated due to performance and cross browser support issues.

$(document).bind('DOMNodeRemoved', function(e) {
  if(e.target.id=='btn') {
    $('#tt').hide();
  }
});

In newer browsers (Chrome 18+, Firefox 14+) there is support for the MutationObserver object, which is designed to replace mutation events. In your case it could work this way:

var target = document.querySelector('body');

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {  
    if(mutation.removedNodes[0]!=undefined) {
      if(mutation.removedNodes[0].id=='btn') {
        $('#tt').hide();              
      }
    }
  });    
});

var config = { attributes: true, childList: true, characterData: true };

observer.observe(target, config);

Here's a link to a question on Code Review where someone is writing a DOM MutationObserver shim which falls back on mutation events when the MutationObserver object isn't available.

Community
  • 1
  • 1
Mathijs Flietstra
  • 12,900
  • 3
  • 38
  • 67
  • 1
    That's a neat solution. Only works if the removal happens via the `.remove()` method though. – mpen Apr 17 '13 at 03:14
  • True, I have updated my answer with some alternative approaches which will work even when the element is removed in other ways. – Mathijs Flietstra Apr 17 '13 at 06:19
  • `MutationObserver` looks pretty sweet. About time they add something like that :-) – mpen Apr 17 '13 at 16:23