May 14, 2005

Extensible XUL Applications and ID Attributes

There's one big problem with XUL applications which expect overlays: the application must define a way for overlay developers to identify their content elements. The id attribute of XUL elements is particularly troublesome. After all, anyone could have this:

<vbox id="contentBox"/>

So if you have that in your overlay, and I have a similar element in my overlay:

<textbox id="contentBox"/>

Immediately one of us has a problem. The application should have defined for us a way to avoid ID collisions like this. At the very least, it breaks the DOM's getElementById method.

A few days ago, I blogged about a JavaScript scope registry. I thought to myself, "if we have a unique scope id for the scripts, I can reuse that as an attribute in the overlay."

So instead of relying on a single ID attribute to identify an element, I can rely on a two-part key: a scopeid attribute on an ancestor element, and a localid attribute on the target node.

Thus, I present a simple function for getting a node by this combination key:

document.getElementByAttrKey = function byAttrKey(aScopeId, aLocalId) {
  var scopeElements = document.getElementsByAttribute("scopeid", aScopeId);
  if (!scopeElements)
    return null;
  for (var ptr = 0; ptr < scopeElements.length; ptr++) {
    var list = scopeElements[ptr].getElementsByAttribute("localid", aLocalId);
    if (!list)
      continue;
    return list[0];
  }
  return null;
}

It is probably more efficient to cache these nodes in a registry, though.

UPDATE: I love it when someone comes up with a cleaner solution than me. Thank you, Mr. Karel. Still, the premise (that id alone is not enough) holds.

Posted by WeirdAl at May 14, 2005 11:41 PM
Comments

Why not to use name spaces? An ID reference would then look like id=myNameSpace.actualID

(From Alex: That's just more characters for people to type and more complicated.)

Posted by: funTomas at May 15, 2005 4:14 AM

Um... XPath!


var xpath = '//[@scopeid="' + aScopeId + '"]/[@localid="'+ aLocalId +'"]';
var nodes = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// return (nodes.snapshotLength > 0) ? nodes.snapshotItem(0) : null;

(From Alex: I feel dumb. That's a better solution, though XPath suggests //(scopeidtest)//(localidtest). :) )

Posted by: Ben Karel at May 15, 2005 9:39 AM