Most efficient method of detecting/monitoring DOM changes?
Asked Answered
M

6

52

I need an efficient mechanism for detecting changes to the DOM. Preferably cross-browser, but if there's any efficient means which are not cross browser, I can implement these with a fail-safe cross browser method.

In particular, I need to detect changes that would affect the text on a page, so any new, removed or modified elements, or changes to inner text (innerHTML) would be required.

I don't have control over the changes being made (they could be due to 3rd party javascript includes, etc), so it can't be approached from this angle - I need to "monitor" for changes somehow.

Currently I've implemented a "quick'n'dirty" method which checks body.innerHTML.length at intervals. This won't of course detect changes which result in the same length being returned, but in this case is "good enough" - the chances of this happening are extremely slim, and in this project, failing to detect a change won't result in lost data.

The problem with body.innerHTML.length is that it's expensive. It can take between 1 and 5 milliseconds on a fast browser, and this can bog things down a lot - I'm also dealing with a large-ish number of iframes and it all adds up. I'm pretty sure the expensiveness of doing this is because the innerHTML text is not stored statically by browsers, and needs to be calculated from the DOM every time it is read.

The types of answers I am looking for are anything from the "precise" (for example event) to the "good enough" - perhaps something as "quick'n'dirty" as the innerHTML.length method, but that executes faster.

EDIT: I should also point out that whilst it would be "nice" to detect the precise element that has been modified, it is not an absolute necessity - just the fact that there has been any change would be good enough. Hopefully this broadens people's responses. I'm going to investigate Mutation Events, but I still need a fallback for IE support, so any whacky, creative, outside-of-the-square ideas would be very welcome.

Mucin answered 16/3, 2010 at 18:26 Comment(3)
Not much luck the last time this was asked: #649496Fudge
Yes, I saw that, and you are right...not much luck. However, because I don't require 100% accuracy, and also because I don't need to pinpoint which element has changed, just that there has been a change, I'm hoping people come up with some creative solutions for me...Mucin
Possible duplicate of Is there a JavaScript/jQuery DOM change listener?Weidman
B
10

http://www.quirksmode.org/js/events/DOMtree.html

jQuery now supports a way to attach events to existing and future elements corresponding to a selector: http://docs.jquery.com/Events/live#typefn

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

Boatload answered 16/3, 2010 at 19:46 Comment(4)
Thanks, this seems to cover all bases - DOMTree events for non-IE and onPropertyChange for IE. I haven't yet implemented the onPropertyChange event but will do - see my comments on @Gaby's post for an update of what's been done so far.Mucin
jQuery's live() method doesn't seem to work the best in IE, so I'm also using a standard bind() (then unbind().bind() on any changes in case anything new has been added), but using the DOMTree events, along with onPropertyChange, and a setInterval fallback, seems to do the trick. watch() seemed like too much hassle (would have to manually add to all elements as it's not a standard event type, it's just a method), and I'm not sure that it's supported properly in various browsers, whilst the other two methods seemed to cover most bases.Mucin
FYI: "As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live()." from docsBozarth
Add'l FYI: mozilla docs say that observing mutation events is deprecated and should be replaced with "mutation observers". I don't know if mutation observers are at all usable yet, though.Bozarth
D
36

To bring this up to date, the DOM4 standard does away with Mutation Events and replaces them with Mutation Observers: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Dorso answered 6/3, 2014 at 21:9 Comment(0)
B
10

http://www.quirksmode.org/js/events/DOMtree.html

jQuery now supports a way to attach events to existing and future elements corresponding to a selector: http://docs.jquery.com/Events/live#typefn

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

Boatload answered 16/3, 2010 at 19:46 Comment(4)
Thanks, this seems to cover all bases - DOMTree events for non-IE and onPropertyChange for IE. I haven't yet implemented the onPropertyChange event but will do - see my comments on @Gaby's post for an update of what's been done so far.Mucin
jQuery's live() method doesn't seem to work the best in IE, so I'm also using a standard bind() (then unbind().bind() on any changes in case anything new has been added), but using the DOMTree events, along with onPropertyChange, and a setInterval fallback, seems to do the trick. watch() seemed like too much hassle (would have to manually add to all elements as it's not a standard event type, it's just a method), and I'm not sure that it's supported properly in various browsers, whilst the other two methods seemed to cover most bases.Mucin
FYI: "As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live()." from docsBozarth
Add'l FYI: mozilla docs say that observing mutation events is deprecated and should be replaced with "mutation observers". I don't know if mutation observers are at all usable yet, though.Bozarth
S
8

Mutation events are the W3 recommendation of what you are looking for..

Not sure if they are supported all around.. (IE will most likely not support them..)

Sulamith answered 16/3, 2010 at 18:34 Comment(2)
I'm now using these events - I'm still checking for body.innerHTML.length at set intervals, but if any of these events are triggered, I make the assumption that the periodical check is no longer required and turn it off. I'm still to investigate onPropertyChange and watch() as alternatives, and will likely implement these using a similar idea, being that if they are triggered, I can cancel any other checks - onPropertyChange might work in IE but not other browsers.Mucin
Mutation events are currently deprecated in favor of Mutation Observers developer.mozilla.org/en-US/docs/Web/API/MutationObserverOkun
C
1

You could try using the DOMNodeInserted and DOMNodeRemoved events. Acording to Quirksmode, they kind of work in most browsers, with the notable exception of IE...

http://www.quirksmode.org/dom/events/index.html

Checkered answered 16/3, 2010 at 18:35 Comment(1)
They are two of the Mutation Event types, that were introduced in DOM level 2. A handful more event types exist..Sulamith
U
1

I have recently written a plugin that does exactly that - jquery.initialize

You use it the same way as .each function

$(".some-element").initialize( function(){
    $(this).css("color", "blue"); 
});

The difference from .each is - it takes your selector, in this case .some-element and wait for new elements with this selector in the future, if such element will be added, it will be initialized too.

In our case initialize function just change element color to blue. So if we'll add new element (no matter if with ajax or even F12 inspector or anything) like:

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Plugin will init it instantly. Also plugin makes sure one element is initialized only once. So if you add element, then .deatch() it from body and then add it again, it will not be initialized again.

$("<div/>").addClass('some-element').appendTo("body").detach()
    .appendTo(".some-container");
//initialized only once

Plugin is based on MutationObserver - it will work on IE9 and 10 with dependencies as detailed on the readme page.

Underdog answered 6/2, 2015 at 17:6 Comment(0)
F
0

jQuery Mutate does this too, by default it supports like height, width, scrollHeight etc... but it also can be extended with a little bit of extra code to add new events like see if text has changed etc...

http://www.jqui.net/jquery-projects/jquery-mutate-official/

Hope it helps

Fireplace answered 10/1, 2013 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.