347

I have a div which has its content changing all the time , be it ajax requests, jquery functions, blur etc etc.

Is there a way I can detect any changes on my div at any point in time ?

I dont want to use any intervals or default value checked.

Something like this would do

$('mydiv').contentchanged() {
 alert('changed')
}
pouyan
  • 3,445
  • 4
  • 26
  • 44
BoqBoq
  • 4,564
  • 5
  • 23
  • 29
  • 5
    Possible duplicate: http://stackoverflow.com/questions/10328102/how-to-detect-content-change-event-on-a-div – Rob Mar 27 '13 at 11:27
  • 17
    @Rob That's binding a `keypress` event to a contenteditable `
    ` element. I'm not sure the solutions there apply to this. They definitely wouldn't pick up any programmatic changes to the content of an element.
    – Anthony Grist Mar 27 '13 at 11:35

13 Answers13

524

If you don't want use timer and check innerHTML you can try this event

$('mydiv').on('DOMSubtreeModified', function(){
  console.log('changed');
});

More details and browser support datas are Here.

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
iman
  • 6,062
  • 1
  • 19
  • 23
  • 64
    this event is deprecated http://www.w3.org/TR/DOM-Level-3-Events/#glossary-deprecated – Isaiyavan Babu Karan May 05 '14 at 09:06
  • 3
    Mozilla 33: falled in recursion for element . Needed to find another way – Chaki_Black Oct 28 '14 at 17:32
  • 20
    It's serious DO NOT use this event it will crash all your work cos it is fired all the time. Instead use the events below $('.myDiv').bind('DOMNodeInserted DOMNodeRemoved', function() { }); – George SEDRA Mar 03 '16 at 14:48
  • 1
    For those looking for a non depreciated answer: [Scroll down](http://stackoverflow.com/questions/15657686/jquery-event-detect-changes-to-the-html-text-of-a-div#answer-15658107) – Josh Mc Mar 09 '16 at 21:29
  • This answer, using on() instead of bind() should be the accepted one – dodoconr May 15 '17 at 19:53
  • 1
    @IsaiyavanBabuKaran please what is your solution to to replicate the exact same code of the answer that isn't deprecated ? – kabrice Aug 08 '17 at 12:36
  • 1
    Mutation events handled in this manner a deprecated - try this: https://gabrieleromanato.name/jquery-detecting-new-elements-with-the-mutationobserver-object/ – Ross Oct 22 '17 at 14:53
  • remember the # '#mydiv' – Pomster Jun 18 '19 at 13:10
  • @Pomster this is just a sample and mydiv is not an Id. it is just like answer sample code. but thanks for mention it. – iman Jun 23 '19 at 09:33
125

Using Javascript MutationObserver:

// Select the target node.
var target = document.querySelector('mydiv')

// Create an observer instance.
var observer = new MutationObserver(function(mutations) {
    console.log(target.innerText);   
});

// Pass in the target node, as well as the observer options.
observer.observe(target, {
    attributes:    true,
    childList:     true,
    characterData: true
});

See the MDN documentation for details; this should work in pretty much all current browser, including IE11.

Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
PPB
  • 2,937
  • 3
  • 17
  • 12
  • 11
    This is the correct answer as this is now favoured over using DOMSubtreeModified – Joel Davey Oct 11 '18 at 11:56
  • 6
    I am getting error with this, even though I have given correct selector. "VM21504:819 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'." – samir Jul 18 '19 at 09:38
  • 1
    @samir The error apppears when your selected element was not found. It could be that the element you are trying to observe does not exist when your code executes. [This answer](https://stackoverflow.com/questions/40398054/observe-on-mutationobserver-parameter-1-is-not-of-type-node) is quite useful – A Friend Sep 28 '20 at 15:42
  • I will this snippet here. `HTMLElement.prototype.onDOMSubtreeModified = function(c, o = {attributes: true, childList: true, characterData: true}){return new MutationObserver((m) => {c.call(this, m);}).observe(this, o);};` – GramThanos Nov 20 '20 at 15:35
76

Since $("#selector").bind() is deprecated, you should use:

$("body").on('DOMSubtreeModified', "#selector", function() {
    // code here
});
Nathan Arthur
  • 8,287
  • 7
  • 55
  • 80
Petar Nikov
  • 1,159
  • 10
  • 17
47

You can try this

$('.myDiv').bind('DOMNodeInserted DOMNodeRemoved', function() {

});

but this might not work in internet explorer, haven't tested it

Soviut
  • 88,194
  • 49
  • 192
  • 260
user1790464
  • 544
  • 3
  • 3
  • 11
    Deprected: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events Use instead: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver – Randall Flagg Oct 15 '15 at 09:38
  • 3
    NOTE!: if you use this for a trigger and there's a lot of changes being made to the page - it will run your function X times (maybe even X=1,000 or more) which could be very inefficient. One simple solution is to define a "running" boolean var, that will... if(running == true){return} ...without running your code if it's already running. Set running=true right after your if logic, and running=false before your function exits. You could also use a timer to limit your function to only be able to run every X seconds. running=true; setTimeout(function(){running=false},5000); (or something better) – JxAxMxIxN Aug 21 '16 at 16:43
  • I used this on a select box that had options being added and removed. It worked great when items were added but the remove seemed to be 1 item behind. When the last option was removed it wouldn't fire. – CodeMonkey Mar 05 '17 at 07:37
  • 2
    @JxAxMxIxN You can also bump the timeout timer by clearing & setting timeout again: `clearTimeout(window.something); window.something = setTimeout(...);` – Ctrl-C May 23 '17 at 11:17
  • agreed - your way is the way to go - since learning Python I've cleared up a lot of my poor coding practices across multiple languages (not all, just a lot ;) – JxAxMxIxN May 24 '17 at 01:38
  • @RandallFlagg please how to use MutationObserver to replicate the exact same code of the answer ? – kabrice Aug 08 '17 at 12:34
36

You are looking for MutationObserver or Mutation Events. Neither are supported everywhere nor are looked upon too fondly by the developer world.

If you know (and can make sure that) the div's size will change, you may be able to use the crossbrowser resize event.

Steve
  • 9,335
  • 10
  • 49
  • 81
rodneyrehm
  • 13,442
  • 1
  • 40
  • 56
  • 1
    This is the one. Specifically, [DOMSubtreeModified](https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference/DOMSubtreeModified). You might find the [mutation-summary](https://code.google.com/p/mutation-summary/) library helpful, and this list of [DOM Tree Events](http://davidwalsh.name/dom-events-javascript). – BenjaminRH May 24 '13 at 09:32
  • 1
    this event is deprecated https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events – Adriano Jul 30 '14 at 11:58
  • 11
    Incase anyone else has had to try read through everything everywhere, this is the correct answer. Mutation Events were supported in past browsers, Mutation Observer is what will is supported in modern browsers and will be supported in the future. See link for support: [CANIUSE Mutation Observer](http://caniuse.com/#feat=mutationobserver) – Josh Mc Mar 09 '16 at 21:25
30

The following code works for me:

$("body").on('DOMSubtreeModified', "mydiv", function() {
    alert('changed');
});
halfer
  • 19,824
  • 17
  • 99
  • 186
Sanchit Gupta
  • 3,148
  • 2
  • 28
  • 36
17

There is no inbuilt solution to this problem, this is a problem with your design and coding pattern.

You can use publisher/subscriber pattern. For this you can use jQuery custom events or your own event mechanism.

First,

function changeHtml(selector, html) {
    var elem = $(selector);
    jQuery.event.trigger('htmlchanging', { elements: elem, content: { current: elem.html(), pending: html} });
    elem.html(html);
    jQuery.event.trigger('htmlchanged', { elements: elem, content: html });
}

Now you can subscribe divhtmlchanging/divhtmlchanged events as follow,

$(document).bind('htmlchanging', function (e, data) {
    //your before changing html, logic goes here
});

$(document).bind('htmlchanged', function (e, data) {
    //your after changed html, logic goes here
});

Now, you have to change your div content changes through this changeHtml() function. So, you can monitor or can do necessary changes accordingly because bind callback data argument containing the information.

You have to change your div's html like this;

changeHtml('#mydiv', '<p>test content</p>');

And also, you can use this for any html element(s) except input element. Anyway you can modify this to use with any element(s).

Kevin Kopf
  • 13,327
  • 14
  • 49
  • 66
SaminatorM
  • 630
  • 5
  • 18
  • To observe and act on changes to a particular element, just modify the changeHtml function to use 'elem.trigger(...)' instead of 'jQuery.event.trigger(...)', and then bind to the element like $('#my_element_id').on('htmlchanged', function(e, data) {...} – KenB Aug 01 '14 at 17:14
  • 12
    "this is a problem with your design and coding pattern", what to do if you include third party scripts therefore you have no control on their source code? but you need to detect their changes to one div? – DrLightman Sep 19 '16 at 12:53
  • @DrLightman a rule of thumb is to choose third party lib with callback event provided – Marcel Djaman Nov 30 '17 at 15:01
11

Use MutationObserver as seen in this snippet provided by Mozilla, and adapted from this blog post

Alternatively, you can use the JQuery example seen in this link

Chrome 18+, Firefox 14+, IE 11+, Safari 6+

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();
Anthony Awuley
  • 3,455
  • 30
  • 20
5

Tried some of answers given above but those fires event twice. Here is working solution if you may need the same.

$('mydiv').one('DOMSubtreeModified', function(){
    console.log('changed');
});
Virang Jethva
  • 165
  • 1
  • 6
2

You can store the old innerHTML of the div in a variable. Set an interval to check if the old content matches the current content. When this isn't true do something.

codelove
  • 1,988
  • 5
  • 26
  • 36
2

Try the MutationObserver:

browser support: http://caniuse.com/#feat=mutationobserver

<html>
  <!-- example from Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/dom/mutation-observers/ -->

  <head>
    </head>
  <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript">
      // Inspect the array of MutationRecord objects to identify the nature of the change
function mutationObjectCallback(mutationRecordsList) {
  console.log("mutationObjectCallback invoked.");

  mutationRecordsList.forEach(function(mutationRecord) {
    console.log("Type of mutation: " + mutationRecord.type);
    if ("attributes" === mutationRecord.type) {
      console.log("Old attribute value: " + mutationRecord.oldValue);
    }
  });
}
      
// Create an observer object and assign a callback function
var observerObject = new MutationObserver(mutationObjectCallback);

      // the target to watch, this could be #yourUniqueDiv 
      // we use the body to watch for changes
var targetObject = document.body; 
      
// Register the target node to observe and specify which DOM changes to watch
      
      
observerObject.observe(targetObject, { 
  attributes: true,
  attributeFilter: ["id", "dir"],
  attributeOldValue: true,
  childList: true
});

// This will invoke the mutationObjectCallback function (but only after all script in this
// scope has run). For now, it simply queues a MutationRecord object with the change information
targetObject.appendChild(document.createElement('div'));

// Now a second MutationRecord object will be added, this time for an attribute change
targetObject.dir = 'rtl';


      </script>
    </body>
  </html>
juFo
  • 17,849
  • 10
  • 105
  • 142
2

DOMSubtreeModified is not a good solution. It can cause infinite loops if you decide to change the DOM inside the event handler, hence it has been disabled in a number of browsers. MutationObserver is the better answer.

MDN Doc

const onChangeElement = (qSelector, cb)=>{
 const targetNode = document.querySelector(qSelector);
 if(targetNode){
    const config = { attributes: true, childList: false, subtree: false };
    const callback = function(mutationsList, observer) {
        cb($(qSelector))
    };
    const observer = new MutationObserver(callback);
    observer.observe(targetNode, config);
 }else {
    console.error("onChangeElement: Invalid Selector")
 }
}

And you can use it like,

onChangeElement('mydiv', function(jqueryElement){
   alert('changed')
})
Tushar Shukla
  • 5,666
  • 2
  • 27
  • 41
0

Adding some content to a div, whether through jQuery or via de DOM-API directly, defaults to the .appendChild() function. What you can do is to override the .appendChild() function of the current object and implement an observer in it. Now having overridden our .appendChild() function, we need to borrow that function from an other object to be able to append the content. Therefor we call the .appendChild() of an other div to finally append the content. Ofcourse, this counts also for the .removeChild().

var obj = document.getElementById("mydiv");
    obj.appendChild = function(node) {
        alert("changed!");

        // call the .appendChild() function of some other div
        // and pass the current (this) to let the function affect it.
        document.createElement("div").appendChild.call(this, node);
        }
    };

Here you can find a naïf example. You can extend it by yourself I guess. http://jsfiddle.net/RKLmA/31/

By the way: this shows JavaScript complies the OpenClosed priciple. :)

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Andries
  • 1,547
  • 10
  • 29
  • It does not work with append child... I actually modify the html of it via other functions. – BoqBoq Mar 27 '13 at 12:32
  • Like removeChild() replaceChild() etc. But you're right on innerHTML. You should avoid it somehow. – Andries Mar 27 '13 at 12:39