How to Query Elements
CSS selector queries have been all the rage since Prototype/jQuery
"changed the way developers write Javascript".(for the worse mostly).
Their primary appeal seems to be that CSS selectors are a familiar
syntax for Web designers, who are typically more into graphics than
programming.
It is really best to avoid CSS selector queries and write simple
wrappers for gEBI, gEBTN and perhaps gEBCN. A well-designed Web
application can get by just fine with variations of those three.
Unlike queries, the implementations of these host methods are rock-
solid (with minor exceptions in legacy IE). In contrast, no version
of IE prior to 8 (and there are lot of corporate users stuck with
those to this day) features a host method for CSS selector queries,
thus requiring ambitious, over-complicated libraries to provide a
rough simulation. These simulations have been proven to return wildly
different results, both compared to each other and to the "standard"
host methods. They are very slow about getting the wrong answers
too:-
http://www.cinsoft.net/slickspeed.html
You certainly can't write reliable cross-browser code calling such
unpredictable functions.
This is a basic query rendition with simplified feature detection*:-
if (document.querySelectorAll) {
var myQuery = function(selector) {
return toArray(document.querySelectorAll(selector));
};
}
...that works with a single document (not elements) and will degrade
in versions of IE before 8 (and compatibility modes). In many
contexts, that's perfectly acceptable. Why include a ton of what has
been proven to be junk code to try to make an enhancement or
application work in IE 7?
A "toArray" example, as well as a "forEach", can be found here:-
http://jsperf.com/iterating-element-array-from-query
Why documents only? Because the "standard" recommendation for element-
based queries doesn't work the way that Web developers have been
conditioned to expect. Rather than try to re-learn queries based on
the W3C recommendations, it is easier to stick with document-rooted
queries.
var els = myQuery('#myid div.myclass');
The above rendition doesn't do frames or other documents besides the
one that hosts the script, but that's an easy upgrade (assuming your
context calls for it):-
if (document.querySelectorAll) {
var myQuery = function(selector, doc) {
return toArray((doc || document).querySelectorAll(selector));
};
}
There's also no exception handling in this example, which is by
design. When could you expect an exception to be thrown? When
implementations don't understand the passed selector. For example, IE
8 has QSA, but has never heard of the more esoteric selectors
introduced in CSS3. So it stands to figure that IE 8 will throw an
exception when passed such a selector. Therefore, if your application
is to work (rather than degrade) in IE 8, you need to stick to
selectors that are supported in IE 8.
The "gateway" for applications that feature queries must detect the
myQuery function *and* test the types of selectors that will be used.
That's where the try-catch belongs.
It's not good enough to do this:-
if (myQuery) {
...
}
...as the function may work for some selectors but not others. The
gateway should look like this:-
if (myQuery && myQueryTestsPassed) {
...
}
So if you insist on using esoteric CSS3 selectors, know that IE 8, as
well as any other "lesser" browsers will degrade. Of course,
degradation requires a usable fallback, but that's another story...
If you stick to basic queries, IE 8+ and virtually all of the rest of
the browsers in use today are going to work the same. It's an
imperfect Web, so feature tests may catch some exceptions, but the
outlook is far more predictable (and much better documented) than the
typical library fare, which typically don't test much of anything and
have no reliable fallback for failures anyway.
Alternatively, you can dump jQuery's "Sizzle" into the mix and hope
that it manages to accurately simulate QSA for all of the older QSA-
challenged browsers still in use. But hoping is not going to stand a
chance against the mountain of evidence to the contrary (see speed
tests). There is a lot of anecdotal "evidence" (as well as dubious
unit tests) out there indicating that jQuery "works" for users that
make sure to use their designated "core browsers", but that's just not
the case. It could hardly "work" without a competently written
attribute value getter, could it?
Put it this way: if you heard that somebody walked across a known mine
field and didn't blow up, would you then consider the site safe for a
sack race? What if a bunch of bloggers confirmed the story? :)
By the same token, if your jQuery-based site seems to work in iOS or
Android, does that mean that it was a good idea to send jQuery to such
platforms? And doesn't the fact that so many Web developers are
either doing just that or breaking ground on lighter, faster non-
jQuery alternative sites (users just love to be redirected to "mobile
sites") tell you that the script is dead weight?
What other query-related "goodies" will you find in the typical
library (or propped up framework)? There's the "is" function (tests
an element against a selector) and the "find" (or is it filter?)
function, which iterates through query results, testing each element
against a selector. Will leave those as exercises.
There's also jQuery's "live", which tangles up queries and event
delegation (with predictable results). Much more on that in a later
edition...
http://www.cinsoft.net/
http://www.twitter.com/cinsoft
http://jsperf.com/browse/david-mark
* Use isHostMethod to detect querySelectorAll.