Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery - run a function when DOM changes

I've been using livequery so far which works, but it makes the page browsing seem really slow. So I'm trying to find a alternative solution for this.

I'm attaching a function that runs some ajax on elements with a certain class, like:

$(".blah").each(function(){
  $.ajax({
      ...
      success: function(data) { 
        $(this).removeClass(".blah");
        // do other stuff
      }
  });
});

Now I have several events hooked on different elements that can append html in the DOM, like:

$(".button").click(function(){
  $.ajax({
      ...
      success: function(data) { 
        $(this).append(data);
        // here, new elements with ".blah" class could be inserted in the DOM
        // the event above won't be fired...
      }
  });
});

How could I make so the first ajax function above runs when the DOM gets updated in the other events?

Update:

I also found this plugin: http://www.thunderguy.com/semicolon/2007/08/14/elementready-jquery-plugin/

Do you think it would be a better solution? From a quick look it seems to, because it allows you to set a "polling" interval, which might decrease cpu usage if you set it to 1 sec or somehting. I'm testing it now :)

Update 2:

Unfortunately it only works with element IDs for some weird reason :(

like image 928
Alex Avatar asked Jan 22 '11 02:01

Alex


4 Answers

Most major browsers (but sadly not IE <= 8) support DOM mutation events (also see MDN). If you're interested in just nodes being added to the DOM, use the DOMNodeInserted event. If you want to be informed about any DOM change, use the catch-all DOMSubtreeModified event.

For example:

document.addEventListener("DOMNodeInserted", function(evt) {
    alert("Node inserted. Parent of inserted node: " + evt.relatedNode.nodeName);
}, false);

document.body.appendChild( document.createElement("div") );

Update 13 December 2012

DOM mutation events are now deprecated and browsers are gradually moving to mutation observers. The most sensible strategy at the moment seems to be to use mutation observers and fall back to mutation events.

like image 166
Tim Down Avatar answered Nov 14 '22 17:11

Tim Down


It's true because the new elements do not have events bound to callbacks, they are just new fresh elements. I would recommend to bind events on the newly created elements just after they are created (i.e. inside the ajax success function):

    $(".blah").each(function(){
      $.ajax({
          ...
          success: function(data) { 
            $(this).removeClass(".blah");
            // add handlers to new elements with class "button"
            $(this).find('.button').click(function() {  
                     $.ajax({
                          success: function(data) { 
                            $(this).append(data);
                          });
          }
      });
    }
like image 2
doc_id Avatar answered Nov 14 '22 17:11

doc_id


I have encountered a problem like this as well. There is no true way to have something run on dom change (that I know of). I have approached this via two different methods

  1. Have a setTimeout calling the function every second or have the function triggered when the user moves the mouse. Both of these can caused slow downs if not handled correctly
  2. Have a function called when the ajax is done. So just have doAjax() called every time you complete a query by having it in the success section.

You could also have the first ajax query bound the body tag, so anytime the user clicks something the ajax event would fire, and then check if the right criteria is met.

Edit: This might work. Add $("BODY ID/CLASS").change(function () {}); Not totally sure, as I am not the most fluent in jQuery.

like image 1
Colum Avatar answered Nov 14 '22 18:11

Colum


IE9+, FF and Chrome :

var observeDOM = (function(){
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
        eventListenerSupported = window.addEventListener;

    return function(obj, callback){
        if( MutationObserver ){
            // define a new observer
            var obs = new MutationObserver(function(mutations, observer){
                if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
                    callback();
            });
            // have the observer observe foo for changes in children
            obs.observe( obj, { childList:true, subtree:true });
        }
        else if( eventListenerSupported ){
            obj.addEventListener('DOMNodeInserted', callback, false);
            obj.addEventListener('DOMNodeRemoved', callback, false);
        }
    }
})();

observeDOM( $('#dom_element')[0] ,function(){ 
    console.log('dom changed');
});
like image 1
vsync Avatar answered Nov 14 '22 17:11

vsync