2

I want to create the equivalent of an onCreate event for all tags which will be created that match a JQuery selector.

For instance, let's consider a document where the result of $("#foo > .bar > ul > li") is an empty set. I have a function called fooBar and I want this function to be called whenever a tag matching $("#foo > .bar > ul > li") was created.

I would like to define this event in my

$(function() {});

Does somebody know a possibility for this?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175

2 Answers2

3

As far as I'm aware there aren't any events that are fired when elements are added to the DOM, so there's nothing you can bind a handler to in order to check for this.

What you can do is set up a polling routine that will periodically check the DOM for elements that match your selector, compare the current number of matches against the previous value, and perform whatever actions you wish if they differ.

var matchedElements = 0;

function poll() {
    var $elements = $("#foo > .bar > ul > li");
    if($elements.length > matchedElements) {
        fooBar();
    }
    matchedElements = $elements.length;
}

setInterval(poll, 500); // runs poll() every half a second

This all assumes that you're not controlling the creation of these elements, or at least aren't controlling them in a way that allows you to reliably know they've been created.

If the only source of these elements is a single function you've written then you could simply extend that to trigger a handler for a custom event bound in jQuery.

Anthony Grist
  • 38,173
  • 8
  • 62
  • 76
  • According to [this list of DOM events](http://en.wikipedia.org/wiki/DOM_events) there is a `DOMNodeInserted` event, maybe that could work? – Josh Davenport-Smith Apr 18 '12 at 13:11
  • In modern browsers there is DOMNodeInserted/DOMSubtreeModified which fire every time something changes "inside" the element those events are bound to ... I don't know if this can be used to effectively check for the creation of certain elements matching a selector though – devnull69 Apr 18 '12 at 13:12
  • @JoshDavenport Possibly. Though according to the Wikipedia article you linked, all of the "DOM_" events are deprecated by the W3C in DOM Level 3, so there's no guarantee it will be supported in all browsers. – Anthony Grist Apr 18 '12 at 13:17
  • After rooting around (mainly thanks to [this comment](http://stackoverflow.com/questions/6659662/why-is-the-domsubtreemodified-event-deprecated-in-dom-level-3#comment10557522_6659678)) it looks like there is an alternative coming in [DOM Level 4](http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#mutation-observers). Support for that is surely not coming any time soon – Josh Davenport-Smith Apr 18 '12 at 13:24
3

Most practical solution

You can hook into the DOMNodeInserted event on document to detect the changes, and use .is to check if they match the selector of your choice.

$(function() {
    var selector = "whatever";
    $(document).on('DOMNodeInserted', function(e) {
        if ($(e.srcElement || e.target).is(selector)) {
            alert("Matching element inserted!");
        }
    });
});​

See it in action.

Compatibility and alternatives

This approach is convenient, but it does have two drawbacks:

  1. The DOMNodeInserted event is deprecated.
  2. It does not work on IE < 9, and cannot be made to work.

As regards the first, I wouldn't consider this an issue. It may be deprecated, but as long as there is no other alternative I really don't think any browser vendor would pull this functionality. Maybe in five years or so this will become a practical issue, but since the code is 10 lines or so total it will surely be easy to update it to work with the latest standard.

For IE compatibility, the sad truth is that you cannot do anything directly. However, you can resort to verbose, horrible hacks that do provide results by modifying the prototypes of DOM elements. See an example tailored to work on IE8.

Sadly, there are multiple issues with this approach:

  • You need to fish out all the methods that can result in the DOM being modified (or at least, all of those you will be using) and weave them into the solution. In the future you will be obliged to check if new DOM mutation methods are added and keep up with supporting them.
  • You need to be extra careful to provide correct replacements for the methods, for all browsers that you choose to target with this (if more than one).
  • Extending the DOM (in general) can be problematic. If you thought this specific method of extension is bad, consider that IE7 does not support it and on that browser you 'd have to replace methods on all elements in the DOM to make sure you hook into every possible modification.
  • Specifically, you cannot target all current browsers with just this code (e.g. Chrome defines these methods on Node.prototype, not on Element.prototype). Targeting future browsers should not be mentioned even if joking.

Finally, you can always decide to use polling to detect changes, as Anthony explains in his answer.

Related resources

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • Worth noting there is fairly good browser support according to [this reference page](http://help.dottoro.com/ljmcxjla.php) despite its deprecation from DOM Level 3, though it is only supported in IE9+ and is buggy in IE9 – Josh Davenport-Smith Apr 18 '12 at 13:27
  • What browser did you develop/test this in? Neither of the buttons produce an alert when clicked for me using Firefox 11.0. From looking at the code I think that clicking the 'Click me' button should produce an alert, and clicking the 'Me too' button shouldn't, correct? – Anthony Grist Apr 18 '12 at 13:29
  • @AnthonyGrist: I made a small bugfix just after initially posting, so perhaps you did not see it when you tried the fiddle? Works fine for me on FF 11, Chrome beta and IE 9. – Jon Apr 18 '12 at 13:31
  • @Jon Yep, I was looking at the previous fiddle. The current link also works for me. – Anthony Grist Apr 18 '12 at 13:32