views:

192

answers:

3

I'd like to be able to identify whether a given DOM node has been appended/inserted into another node yet, or whether it is fresh out of document.createElement() or similar and has not been placed anywhere.

In most browsers just checking the parentNode works.

if (!node.parentNode) {
  // this node is not part of a larger document
}

However, in Internet Explorer it appears that new elements, even right after they've been created with document.createElement() already have a parentNode object (of type DispHTMLDocument??).

Any other nice cross-browser and reliable way?

Edit: looks like Internet Explorer is implicitly creating a DocumentFragment (with nodeType of 11) and setting that as the node's parentNode property.

+5  A: 

I think that even without IE's foibles, checking for the presence of parentNode might not be enough. For example:

var d = document.createElement('div');
var s = document.createElement('span');
d.appendChild(s);
if (s.parentNode) {
    // this will run though it's not in the document
}

If something is in the document, then eventually one of its ancestors will be the document itself. Try this out and see how it goes:

function inDocument(node) {
    var curr = node;
    while (curr != null) {
        curr = curr.parentNode;
        if (curr == document) return true;
    }
    return false;
}

// usage: 
// if (inDocument(myNode)) { .. }

If you only want to check to a certain depth - that is, you know that your newly created elements aren't going to be nested any further than IE's Fragment, try this:

function inDocument(node, depth) {
    depth = depth || 1000;
    var curr = node;
    while ((curr != document) && --depth) {
        curr = curr.parentNode;
        if (curr == null) return false;
    }
    return true;
}

inDocument(myNode, 2);  // check only up to two deep.
inDocument(myNode);     // check up to 1000 deep.
nickf
Thanks nickf - in my case however I'm only worried about the immediate parent - I don't mind if its grandparent, or an ancestor, is not part of the document element, just whether that given element has been 'appended' or 'inserted' anywhere yet. So in other words in your first code example I'm not concerned that d isn't inserted somewhere, as s *is*.
thomasrutter
@thomasrutter in that case, you could short-circuit the loop after two hops. The parentNode of the DocumentFragment which IE creates should hopefully be null.
nickf
Yours is the most concise answer to "how to you tell if a node has been inserted into the document". I'm just a failure at expressing what I really wanted.
thomasrutter
+1  A: 

I've found an answer to my own question. Sorry! I seem to be doing that a lot lately.

Document fragments have a nodeType of 11, and are never inserted into the document, so you can check it like this:

if (!node.parentNode || node.parentNode.nodeType == 11) {
  // this node is floating free
}

You only need a Document fragment when you are inserting more than one peer node. IE implicitly creates one for all newly created nodes though. Anyway checking the nodeType for 11 works.

thomasrutter
That might not work if the node of interest is nested. It's parentNode property will be defined, and parentNode's nodeType may be something other than 11 (Element Node or 1 for example). Nick's solution seems to be a sureshot way of knowing if a node is attached.
Anurag
But that's what I want. If it is 'nested' as you say, I consider that as having been inserted into an element. I want to detect nodes that are free-floating, and my definition of that was "with no parent". Now because of IE's behaviour I've had to expand that definition to "with no parent, or a DocumentFragment as a parent".
thomasrutter
+2  A: 

DOM level 3 introduced the compareDocumentPosition method for a Node that gives positional information on how two nodes are related to each other. One of the return values is DOCUMENT_POSITION_DISCONNECTED meaning the nodes are not connected to each other. Could use this fact to check if a node is not contained inside another node using:

Boolean(parent.compareDocumentPosition(descendant) & 16)

DOCUMENT_POSITION_DISCONNECTED = 0x01;
DOCUMENT_POSITION_PRECEDING    = 0x02;
DOCUMENT_POSITION_FOLLOWING    = 0x04;
DOCUMENT_POSITION_CONTAINS     = 0x08;
DOCUMENT_POSITION_CONTAINED_BY = 0x10;
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;

Google has written a cross-browser implementation (I think, no mention of IE there) of a contains function that can be found at http://code.google.com/p/doctype/wiki/ArticleNodeContains. You could use that for checking if a given node is a descendant of document

.contains(document, someNode)
Anurag