Anne van Kesteren

Selectors API

The last couple of weeks I took some time to draft a Selectors API. Indeed, an API for the Selectors specification. Example of usage:

var x = document.matchAll("div.section > p");
for(i=0; i < x.length; i++){
 // do something with x[i]
}

You can use namespaces as well by passing an XPathNSResolver object (or Function) as second (optional) argument to document.matchAll. There is also document.match which returns the first Element node encountered in the document (using specific traversal). Both methods take a group of selectors as argument, so the following is possible as well:

var y = document.match("#bar, #foo, #baz");

y contains a reference to Element node first in the document that had one of those ID values. I hope to get it on Recommendation track soonish, the document is pretty stable. The biggest problem is probably document.matchAll(":visited"), but that is already possible more or less anyway.

Comments

  1. Regarding the :visited pseudo class, you can already hack a user’s history by using getComputedStyle on a link. Add a bit of Ajax magic and well, you can see where I’m going...

    Posted by Dean Edwards at

  2. Any particular reason for not choosing a name that is more in par with the existing DOM names, e.g. getElementsBySelector?

    ~Grauw

    Posted by Laurens Holst at

  3. (.match makes me think more of regular expressions, both because XPath uses the term and JavaScript has a similar-named function for strings.)

    ~Grauw

    Posted by Laurens Holst at

  4. Laurens there's nothing stopping you from prototyping your own, you know. It's hardly a big deal if it's not already there.

    Posted by jbot at

  5. Anne,
    
    it would be nice if the interface would be implemented by Element
    instead of Document. That is, something like
      var div = document.getElementById('x');
      var list = div.matchAll('something');
    
    Of course this example could be rewritten to
      document.matchAll('#x something'),
    the point is that you might have a reference to an element
    without an id (say, after an event using e.target) and you'd like
    to get some children of that node.
    
    This is similair to getElementsByTagName, which is also 
    implemented by Element.
    
    (And I agree that .match is confusing with the String.match 
    method for regexps. I'd prefer something like .select)

    Posted by Salar at

  6. jbot, aren’t we talking about a work-in-progress of an official W3C specification here? I’d say that deserves some careful thinking of sensible and consistent naming, and I’m just giving my 2 eurocents. Of course I know I can prototype everything, but that’s a bit of a cop-out answer, and beside the point.

    Salar, imho it would be nice if it wer both implemented by Element and Document (just like getElementsByTagName). But otoh, that might not make sense from a CSS specification point of view.

    Posted by Laurens Holst at

  7. Why was a StaticNodeList chosen over a normal live NodeList? Are there any specific implementation concerns with the live list? If so, what are they? Or is it because a static list is easier to work with from an authoring perspective, in that it doesn't inadvertently change content when the script modifes the DOM, which can cause problems when you're iterating over the list?

    What's the reason for having two separate methods: match() and matchAll()? Is there something that match() can do that matchAll().item(0) can't? Is there a use case for where the the author would only want to work with the first matching node that warrants having a special method for it?

    I can imagine that as being a source of confusion for authors, where they use match() without thinking, when they want to select all matching nodes, not just the first, and then have trouble trying to work out why their script isn't working properly.

    Posted by Lachlan Hunt at

  8. Salar, you could always set an ID on the event target, but it seems to be worth considering.

    Lachlan, implementors told me a live NodeList is probably not really feasible. Think of document.matchAll(":hover") for example (and more complicated variants). match is there for speed mostly.

    Posted by Anne at

  9. I think if the user wants to get only one node, he/she will prefer getElementById, instead of using selector. Or perheps getElementsByCSS().items(0) is good enough too.

    But I agree that it should be implemented by Document, not Element.

    Posted by minghong at

  10. Lachlan, Live lists are trouble. They are problematic from the implementation point of view. (Check out the source code of a DOM implementation.) Moreover, they don’t work intuitively for the API user.

    I think XOM-style comatose lists make the most sense from the API user point of view. (The list itself is not live but it holds references to the live nodes—not copies of the nodes.)

    BTW Anne, the autocomplete of Opera 9 allowed me to pick a value for the email field that was later rejected. I chose ‘"Henri Sivonen" <hsivonen@iki.fi>’ from the dropdown. It showed up as ‘"Henri Sivonen"’ and was rejected when I clicked Preview.

    Posted by Henri Sivonen at

  11. Laurens, you're right. I didn't mean to write "Element instead of Document" but Element along with Document, just like getElementsByTagName indeed.

    Posted by Salar at

  12. I agree that the names match and matchAll doesn't quite fit. select and selectSingleNode or rather getElementsBySelector and getElementBySelector are much better. Other than that, this looks good.

    Posted by Asbjørn Ulsberg at

  13. Having thought about it. I agree with Salar. This should be extended to elements as well.

    But I'm just glad that you didn't call it "$". ;-)

    Posted by Dean Edwards at

  14. I want to point you at Simon Willison's getElementsBySelector() function, which is also used in Ben Nolan's Behaviour. Although not a full implementation of the CSS3 selectors, it is extremely useful. In Willison's words:

    It has been pointed out that the function does not have the capability to deal with chained selectors such as div#main.layoutDiv. I have no intention of supporting this kind of advanced CSS syntax - one of my principle design aims is to keep the code short and simple, and implementing a full parser would add a lot of bulk and complexity without significantly improving the utility of the function.

    Posted by Rowan Rodrik van der Molen at