My favorites | Sign in
Project Logo
             
Search
for
Updated Nov 15, 2008 by pilgrim
ArticleNodeContains  
HOWTO determine if one node contains another (goog.dom.contains)

The DOM is hierarchical; some nodes can contain other nodes. So you would think that it would be straightforward to determine whether node A "contained" node B -- that is, whether node B is a descendant of node A. And you would be wrong. It is insanely complicated. It is made even more complicated by the fact that Safari used to offer a broken contains functions, but has since fixed it.

The code

This code relies on functions explained elsewhere:

/**
 * Safari contains is broken, but appears to be fixed in WebKit 522+
 * @type {Boolean}
 * @private
 */
goog.dom.BAD_CONTAINS_SAFARI_ = goog.userAgent.SAFARI &&
    goog.userAgent.compare(goog.userAgent.VERSION, '521') <= 0;

/**
 * Whether a node contains another node
 * @param {Node} parent The node that should contain the other node
 * @param {Node} descendant The node to test presence of
 * @return {Boolean}
 */
goog.dom.contains = function(parent, descendant) {
  // We use browser specific methods for this if available since it is faster
  // that way.

  // IE / Safari(some) DOM
  if (typeof parent.contains != 'undefined' && !goog.dom.BAD_CONTAINS_SAFARI_ &&
      descendant.nodeType == goog.dom.NodeType.ELEMENT) {
    return parent == descendant || parent.contains(descendant);
  }

  // W3C DOM Level 3
  if (typeof parent.compareDocumentPosition != 'undefined') {
    return parent == descendant ||
        Boolean(parent.compareDocumentPosition(descendant) & 16);
  }

  // W3C DOM Level 1
  while (descendant && parent != descendant) {
    descendant = descendant.parentNode;
  }
  return descendant == parent;
};

The code walkthrough

First we need to check whether we're dealing with a broken version of Safari:

goog.dom.BAD_CONTAINS_SAFARI_ = goog.userAgent.SAFARI &&
    goog.userAgent.compare(goog.userAgent.VERSION, '521') <= 0;

Now we need to do some browser-specific voodoo to determine which function to call. We use object detection as much as possible, but we still need a user-agent hack to detect broken versions of Safari.

The first method works on Microsoft Internet Explorer and non-broken versions of Safari.

  if (typeof parent.contains != 'undefined' && !goog.dom.BAD_CONTAINS_SAFARI_ &&
      descendant.nodeType == goog.dom.NodeType.ELEMENT) {
    return parent == descendant || parent.contains(descendant);
  }

The second method is defined in W3C DOM Level 3; it works in Mozilla Firefox, and in Opera 9.5 and later.

  if (typeof parent.compareDocumentPosition != 'undefined') {
    return parent == descendant ||
        Boolean(parent.compareDocumentPosition(descendant) & 16);
  }

The third method is slower but should work in all other browsers.

  while (descendant && parent != descendant) {
    descendant = descendant.parentNode;
  }
  return descendant == parent;
};

Further reading


Sign in to add a comment
Hosted by Google Code