jQuery:如何监听DOM更改?

是否有可能,我怎样才能听取整个DOM树与jQuery的变化?

My specific issue: I have a 'tooltip' function that displays the contents of the title attribute in a stylish way when you do a hover on any html element. When you do a hover, however, by standard the browser renders the title in its own box. I would like to supress that. So what I've thought of is to move the contents of the title attribute to a custom (HTML5) data-title attribute the first time the page is loaded, and then my tooltip function will work with data-title.

The problem is that later on I might add / remove / change the HTML dynamically, so I need to 'rebind' those elements – change those title attrs again. It would be nice if there was an event listener that would listen for such changes for me and rebind the elements automatically.

My best guess is that you want to listen to DOM mutation events.
You can do that by DOM mutation event as any normal javascript event such as a mouse click.

Refer to this : W3 MutationEvent

Example:

$("element-root").bind("DOMSubtreeModified", "CustomHandler");

[edited in reply to research by member Tony]

So, without additional code, this is a bit of a blind shot, but it seems to me there are two things to think about here: 1. the default browser tooltip behaviour; 2. a potentially updated DOM and the ability for your custom tooltips to continue functioning.

Regarding #1: when you bind your custom event to the element, you can use event.preventDefault() so that the tooltips don't appear. This doesn't work properly. So, the workaround to keep using the "title" attribute is to grab the value, push it into the data object (the $.data() function), and then null the title with an empty string (removeAttr is inconsistent). Then on mouseleave, you grab the value out of the data object and push it back into the title. This idea comes from here: How to disable tooltip in the browser with jQuery?

Regarding #2: instead of re-binding on DOM change, you just need to bind once to a listener element that is never expected to be destroyed. Usually this is a container element of some sort, but it can even be document (approximating .live() which is now deprecated) if you really need an all-encompassing container. Here's a sample that uses some fake markup of my own devising:

var container = $('.section');

container.on('mouseenter', 'a', function() {
    var $this = $(this);
    var theTitle = $this.attr('title');
    $this.attr('title', '');
    $('#notatooltip').html(theTitle);   
    $.data(this, 'title', theTitle);
});

container.on('mouseleave', 'a', function() {
    $('#notatooltip').html('');
    var $this = $(this);
    var storedTitle = $.data(this, 'title');
    $this.attr('title', storedTitle);
});

My unrealistic markup (just for this example) is here:

<div class="section">
  <a href="#" title="foo">Hover this foo!</a>
  <div id="notatooltip"></div>
</div>

And a fiddle is here: http://jsfiddle.net/GVDqn/
Or with some sanity checks: http://jsfiddle.net/GVDqn/1/

There's probably a more optimal way to do this (I honestly didn't research if you could bind two separate functions for two separate events with one selector) but it'll do the trick.

You shouldn't need to re-bind based on DOM change, the delegated listener will automatically handle it. And you should be able to prevent default tooltip functionality just by preventing it.

As noted by Greg Pettit, you should be using the on() function on the element.

What this does is allows you to bind a selector to an event, then jQuery will add this event handler when the objects returned by the selector are available.

If you wanted a function to fire on a mouse over event and you wanted it to fire on all elements with the class of *field_title* you would do this:

$('.field_title').bind('mouseenter', function() { doSomething(); });

This will trigger on the over mouse over event on any objects that have the class of *field_title* and execute the function doSomething().

Hope that makes sense 🙂

Interesting Posts