Alternative to DOMNodeInserted
Solution 1
If you are creating a web app that targets recent mobile phones and newer versions of browsers (Firefox 5+, Chrome 4+, Safari 4+, iOS Safari 3+, Android 2.1+), you can use the following code to create an awesome event for the insertion of dom nodes, and it even runs on the nodes initially part of the page's static mark-up!
Here's the link to the full post with and example: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/
Note on Mutation Observers: while the newer Mutation Observers features in recent browsers are great for monitoring simple insertions and changes to the DOM, do understand that this method can be used to do far more as it allows you to monitor for any CSS rule match you can thing of. This is super powerful for many use-cases, so I wrapped this up in a library here: https://github.com/csuwildcat/SelectorListener
You'll need to add the appropriate prefixes to the CSS and animationstart event name if you want to target various browsers. You can read more about that in the post linked to above.
The basic node insertion case
CSS:
@keyframes nodeInserted {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
div.some-control {
animation-duration: 0.01s;
animation-name: nodeInserted;
}
JavaScript:
document.addEventListener('animationstart', function(event){
if (event.animationName == 'nodeInserted'){
// Do something here
}
}, true);
Listening for more complex selector matches:
This enables things that are almost impossible to do with Mutation Observers
CSS:
@keyframes adjacentFocusSequence {
from {
outline-color: #fff;
}
to {
outline-color: #000;
}
}
.one + .two + .three:focus {
animation-duration: 0.01s;
animation-name: adjacentFocusSequence;
}
JavaScript:
document.addEventListener('animationstart', function(event){
if (event.animationName == 'adjacentFocusSequence'){
// Do something here when '.one + .two + .three' are
// adjacent siblings AND node '.three' is focused
}
}, true);
Solution 2
One new alternative that @naugtur briefly mentioned is MutationObserver. It's designed as a replacement for the deprecated mutation events, if the browser(s) you're developing for supports it (like if you're developing a browser extension).
Comments
-
Shane Larson over 2 years
DOMNodeInserted is known to make dynamic pages slow, MDN even recommends not using it altogether, but doesn't provide any alternatives.
I'm not interested in the element inserted, I just need to know when some script modifies the DOM. Is there a better alternative to mutation event listeners (maybe getElementsByTagName inside an nsiTimer)?
-
Šime Vidas over 12 yearsYou need that feature for the entire DOM? And at all times?
-
Wladimir Palant over 12 yearsIf you have a particular script and you know how it works - you can "booby-trap" the DOM methods/properties it uses. Otherwise you are out of luck, monitoring all DOM modifications is exactly what makes mutation events slow.
-
Shane Larson over 12 yearsŠime Vidas, yes I do, some scripts insert <object> tags when the user interacts with the page.
-
Šime Vidas over 12 years@Fabio Are those scripts third-party or your scripts?
-
Shane Larson over 12 yearsThird-party. I have an extension that keeps track of content in webpages.
-
Radu over 12 yearsSo are you interested in
object
or the entire DOM? -
Shane Larson over 12 yearsI'm interested in knowing when my DOM changes so I can take apropriate action. In my case I'm interested in media elements inserted in the page (<embed>, <object>, <video>).
-
csuwldcat over 11 years@Fábio checkout the new answer I added, I believe you'll like it ;)
-
-
Admin over 12 yearsThis is terrible code that will massacre any page it is inserted into.
-
Anonymous over 12 yearsNot true, but great comment. You can change that last little "1" to 500 if you want. That would update every half second instead of (up to) 1000 times per second.
-
Admin over 12 yearsGo to a big site like msnbc.com and note how many big the
getElementsByTagName("*")
collection is. I got "2605" elements. You can't seriously think this is a good idea. -
Rufus over 11 yearsThis is by far the most excellent alternative to
DOMNodeInserted
I've ever seen. In fact I have been using it for months and it works well. -
naugtur over 10 yearsThe technique can be applied to IE10+ but
clip: rect
doesn't work in IE10 (not sure about 11). I usedoutline
in myinsertionQuery
lib github.com/naugtur/insertionQuery -
csuwldcat about 10 years@naugtur outline is a good idea, especially if you can do the dummy animation on just the color. I'll probably update this thread and my blog post with that.
-
csuwldcat about 10 years@naugtur it's now updated on Github, and I've tweaked this answer to match - thanks for the heads-up!
-
naugtur about 10 yearsSorry to say that, but now you're just confusing people. The code in your post is far from being cross-browser (animation events have prefixes, animation itself needs other prefixes) and I never said you can animate just the color. It's not what I tested.
-
naugtur about 10 years@csuwldcat I'm really sorry you got offended. I wasn't scolding anyone, I'll respond more carefully in the future. Your first response to my comment looked to me like a misunderstanding. Tweaking the So answer to be more cross-browser while it didn't mention prefixes is confusing to some readers. A reminder 'use prefixes for animation event' would suffice.
-
csuwldcat about 10 years@naugtur I guess this may be a misunderstanding given your last comment. My point was, that I had already included in the answer the exact reminder you are talking about --> "You'll need to add the aproprieate prefixes to the CSS and animationstart event name if you want to target various browsers. You can read more about that in the post linked to above." It has been located just above the first code block since the beginning ;)
-
Leo almost 10 yearsThe only downside is that element needs to be visible on the page in order for browser to trigger animation for it. If you insert anything that is hidden, nothing will happen - of course, not even when you show it afterwards.
-
csuwldcat almost 10 years@Leo this is true, and I was aware of it. It's a small limitation, but for the ability to observe the match of any style rule, it's a small nit.
-
Leo almost 10 years@csuwldcat I agree, it's a great trick. However, we have a very dynamic UI and I was hoping on using this approach to automatically trigger code that replaces selects with a richer widget, adds tooltips to elements etc., as soon as they are added into the DOM. That way, we don't need to take care of calling the method that does this from different places in the code where elements are added...But we do have panels with such elements which are shown on user action, so it's a deal breaker, unfortunately...
-
csuwldcat almost 10 yearsFor your use, I would checkout X-Tag (x-tags.org) or Polymer (polymer-project.org) - Web Components do exactly what you want.
-
csuwldcat over 9 yearsHowever, Mutation Observers do not provide notification of CSS selector-based mutations though, which is a super helpful feature. I have a small module that wraps the keyframe solution above to make CSS selector listeniong even easier: github.com/csuwildcat/SelectorListener
-
Barney over 9 yearsThink @csuwldcat's advice is outdated – Mutation Observers can detect any change you might infer from a CSS selector (using
attributes
), and more (node removal, text node changes). -
csuwldcat over 9 years@Barney a couple of things: 1) you can listen for selector matches with this method, which is impossible with Mutation Observers - for more on what this provides see: github.com/csuwildcat/SelectorListener, and 2) Mutation Observers aren't supported in older browsers like IE9, Safari 5, 6, etc.
-
csuwldcat over 9 years@Barney I've updated my answer with another use-case that makes it clear what this method can do easily that Mutation Observers cannot.
-
Barney over 9 yearsExcellent demo of the simplicity of your lib's implementation. Native DOM would be more verbose (and less compliant), but all the examples (including @Leo's) can be achieved with
MutationObservers
andElement.matches( cssSelector )
, AFAICT. -
csuwldcat over 9 years@Barney not true: Mutation Observers don't catch things like focus, so you would need to create a much longer, more complex bit of code, that included both a logic-heavy Observer and a focus event listener, to do the equivalent of what I show in the demo. If you look on GitHub at SelectorListeners, it delivers you this unlimited flexibility in a single-line of code.
-
csuwldcat over 9 years@Barney remember, CSS rules encompass not only mutations and DOM sequencing, but many other node states that are infeasible or impossible to detect without listening for a rule match ;)
-
Barney over 9 yearsIt's an excellent library — the DOM API would need a lot of forking to achieve the same thing. Good catch on the pseudo-class selectors!
-
Barney over 9 yearsBTW, not 100% certain on this but I think one thing your library can detect but the native DOM API can't is generated content.
-
aefxx over 7 yearsHahaha, I just had to log in to applaud you guys ... these comments are pure gold, indeed.
-
Matt W over 3 yearsYeah the
*
operator in CSS alone is expensive. Please don't addalert(...)
too. And the 1ms poll omg!