115

change() function works and detects changes on form elements, but is there a way of detecting when a DOM element's content was changed?

This does not work, unless #content is a form element

$("#content").change( function(){
    // do something
});

I want this to trigger when doing something like:

$("#content").html('something');

Also html() or append() function don't have a callback.

Any suggestions?

Elzo Valugi
  • 27,240
  • 15
  • 95
  • 114

15 Answers15

70

I know this post is a year old, but I'd like to provide a different solution approach to those who have a similar issue:

  1. The jQuery change event is used only on user input fields because if anything else is manipulated (e.g., a div), that manipulation is coming from code. So, find where the manipulation occurs, and then add whatever you need to there.

  2. But if that's not possible for any reason (you're using a complicated plugin or can't find any "callback" possibilities) then the jQuery approach I'd suggest is:

    a. For simple DOM manipulation, use jQuery chaining and traversing, $("#content").html('something').end().find(whatever)....

    b. If you'd like to do something else, employ jQuery's bind with custom event and triggerHandler

    $("#content").html('something').triggerHandler('customAction');
    
    $('#content').unbind().bind('customAction', function(event, data) {
       //Custom-action
    });
    

Here's a link to jQuery trigger handler: http://api.jquery.com/triggerHandler/

Kyle Cureau
  • 19,028
  • 23
  • 75
  • 104
  • 1
    final thought. although that should be pretty comprehensive above: you could update the value of a hidden input field with that of the #content html and then bind the change event to the hidden input field :) lots of options – Kyle Cureau Sep 01 '10 at 09:49
  • 7
    +1 "Find where the manipulation occurs, and then add whatever you need to there." Fixed my issue in 2 seconds without any hacks/plugins :) – Hartley Brody Mar 29 '12 at 01:21
  • Only change I suggest here is that the `bind` is expecting your handler to return a `bool`. – Serj Sagan Oct 30 '15 at 19:39
26

These are mutation events.

I have not used mutation event APIs in jQuery, but a cursory search led me to this project on GitHub. I am unaware of the project's maturity.

Babiker
  • 18,300
  • 28
  • 78
  • 125
jrharshath
  • 25,975
  • 33
  • 97
  • 127
  • 17
    This mutation events plugin won´t work, because it just hooks the events into the jQuery mutation methods. They are fired when jQuery itself tries to change the elements, but not when the elements gets changed from outside jQuery. They don't watch changes on the document. Check out this little plugin instead: http://stackoverflow.com/questions/3233991/jquery-watch-div/3234646#3234646 – Sebastián Grignoli Jul 13 '10 at 21:49
  • 10
    Googling and posting a link to a library you've never tried isn't terribly useful. (Commenting because I was prompted to upon downvoting.) – Stop Slandering Monica Cellio Nov 28 '12 at 16:31
  • 28
    [**Mutation events are deprecated**](http://www.w3.org/TR/DOM-Level-3-Events/#events-mutationevents); do not use them. – Brock Adams Mar 01 '13 at 06:59
  • The alternative to mutation events seem to be [MutationObservers](http://stackoverflow.com/a/11106031/2126442). – WoodrowShigeru Apr 05 '15 at 17:29
16

what about http://jsbin.com/esepal/2

$(document).bind("DOMSubtreeModified",function(){
  console.log($('body').width() + ' x '+$('body').height());
})

This event has been deprecated in favor of the Mutation Observer API

FDisk
  • 8,493
  • 2
  • 47
  • 52
15

The browser will not fire the onchange event for <div> elements.

I think the reasoning behind this is that these elements won't change unless modified by javascript. If you are already having to modify the element yourself (rather than the user doing it), then you can just call the appropriate accompanying code at the same time that you modify the element, like so:

 $("#content").html('something').each(function() { });

You could also manually fire an event like this:

 $("#content").html('something').change();

If neither of these solutions work for your situation, could you please give more information on what you are specifically trying to accomplish?

TM.
  • 108,298
  • 33
  • 122
  • 127
11

Try to bind to the DOMSubtreeModified event seeign as test is also just part of the DOM.

see this post here on SO:

how-do-i-monitor-the-dom-for-changes

Community
  • 1
  • 1
Colin
  • 10,630
  • 28
  • 36
10

Try this, it was created by James Padolsey(J-P here on SO) and does exactly what you want (I think)

http://james.padolsey.com/javascript/monitoring-dom-properties/

Elzo Valugi
  • 27,240
  • 15
  • 95
  • 114
Pim Jager
  • 31,965
  • 17
  • 72
  • 98
  • For a cross browser version of Object.prototype.watch see https://gist.github.com/384583 – Radek Jan 27 '12 at 16:20
7

And with HTML5 we have native DOM Mutation Observers.

Elzo Valugi
  • 27,240
  • 15
  • 95
  • 114
3

It's not strictly a jQuery answer - but useful to mention for debugging.

In Firebug you can right-click on an element in the DOM tree and set up 'Break on Attribute Change':

Element right-click in Firebug with Break on Attribute Change highlighted

When an attribute is changed in a script, the debug window will appear and you can track down what it going on. There is also an option for element insertion and element removal below (unhelpfully obscured by the popup in the screengrab).

John Reid
  • 1,155
  • 10
  • 19
  • 1
    This is helpful, but not exactly what he was asking for. This will detect things like when a class changes, or when a style attribute is added or changed. It won't check when the contents on the inside of the node changes. For example, if some javascript somewhere changes a node from hello to World, this will not detect it – Joshua Soileau Jan 19 '16 at 19:43
2

I'm developing tiny JS library called mutabor (https://github.com/eskat0n/mutabor) which intended to simplify usage of DOM Mutation Events. See demo.html for examples.

Eskat0n
  • 937
  • 9
  • 21
1

You can add a callback option to html (,or any) function:

$.fn.oldHtml = $.fn.html;
$.fn.html = function(html,fn){
  fn = fn || function(){};
  var result =  this.oldHtml(html);
  fn();
  return result;
};
$('body').html(11,function(){alert("haha");});

Demo here.

You do the change on some element, not the element is forced to change by something that you have to catch.

  • I had to solve this problem today. I couldn't edit the code which actually called html() because it's in a 3rd party library. A modified version of this was perfect for me. I did this: $.fn.oldHtml = $.fn.html(); $.fn.html = function(e) {var result = this.oldHtml(e); this.trigger('afterHtmlChange',e);} Now I can bind any events I want to the newly created afterHtmlChange event on any element, which will fire whenever anything calls jQuery's html() fn. – Daniel Howard Jan 02 '14 at 18:47
  • Whoops, slight mistake. Fn in my comment above should be: function(e) {var result = this.oldHtml(e); this.trigger('afterHtmlChange',e); return result;} – Daniel Howard Jan 02 '14 at 18:53
1

Try the livequery plugin. That seems to work for something similar I am doing.

http://docs.jquery.com/Plugins/livequery

Arpit
  • 2,207
  • 1
  • 18
  • 29
1

Often a simple and effective way to achieve this is to keep track of when and where you are modifying the DOM.

You can do this by creating one central function that is always responsible for modifying the DOM. You then do whatever cleanup you need on the modified element from within this function.

In a recent application, I didn't need immediate action so I used a callback for the handly load() function, to add a class to any modified elements and then updated all modified elements every few seconds with a setInterval timer.

$($location).load("my URL", "", $location.addClass("dommodified"));

Then you can handle it however you want - e.g.

setInterval("handlemodifiedstuff();", 3000); 
function handlemodifiedstuff()
{
    $(".dommodified").each(function(){/* Do stuff with $(this) */});
}
Antony Carthy
  • 5,549
  • 9
  • 34
  • 38
0

I wrote a snippet that will check for the change of an element on an event.

So if you are using third party javascript code or something and you need to know when something appears or changes when you have clicked then you can.

For the below snippet, lets say you need to know when a table content changes after you clicked a button.

$('.button').live('click', function() {

            var tableHtml = $('#table > tbody').html();
            var timeout = window.setInterval(function(){

                if (tableHtml != $('#table > tbody').
                    console.log('no change');
                } else {
                    console.log('table changed!');
                    clearInterval(timeout);
                }

            }, 10);
        });

Pseudo Code:

  • Once you click a button
  • the html of the element you are expecting to change is captured
  • we then continually check the html of the element
  • when we find the html to be different we stop the checking
Reinderien
  • 11,755
  • 5
  • 49
  • 77
Andrew Atkinson
  • 4,103
  • 5
  • 44
  • 48
0

We can achieve this by using Mutation Events. According to www.w3.org, The mutation event module is designed to allow notification of any changes to the structure of a document, including attr and text modifications. For more detail MUTATION EVENTS

For Example :

$("body").on('DOMSubtreeModified', "#content", function() {
    alert('Content Modified'); // do something
});
Sanchit Gupta
  • 3,148
  • 2
  • 28
  • 36
  • This has been deprecated for a while. One should use [Mutation Observers](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) (same concept) – Neithan Max Aug 08 '17 at 22:47
-3

Not possible, I believe ie has a content changed event but it is certainly not x-browser

Should I say not possible without some nasty interval chugging away in the background!

redsquare
  • 78,161
  • 20
  • 151
  • 159