Unveiling Sizzle.js: How jQuery’s Fast Selector Engine Works
This article dissects the inner workings of jQuery’s Sizzle selector engine, explaining its tokenization, compilation, matcher generation, and performance optimizations, while offering practical tips for writing faster CSS selectors in modern web development.
Remember the once‑famous jQuery library and its claim of having the industry’s fastest DOM selector, Sizzle? By analyzing Sizzle.js source code we can understand the clever design that made it so quick, offering insights useful for modern framework design.
Now we move to the select function and its overall flow
1. Perform lexical analysis to obtain a token list.
2. If a seed set exists, jump straight to the compilation stage.
3. If no seed set and the selector is a single group (no commas), first try to narrow the context: when the first token is an ID selector,
Expr.find["ID"]finds the context and removes the ID token.
4. Then attempt to find a seed set by scanning tokens from right to left; encountering a combinator (>, +, ~, space) stops the scan, otherwise
Expr.findmethods try to locate matching DOM elements, which become the seed set.
5. Enter the compilation phase, where the goal is to reduce the query range as much as possible before actual matching.
The
Expr.findobject maps selector types to native DOM methods:
<code>{
"ID": context.getElementById,
"CLASS": context.getElementsByClassName,
"NAME": context.getElementsByName,
"TAG": context.getElementsByTagName
}</code>Each entry returns a function that validates whether a given element matches the specified type. For example, the CLASS finder receives a class name and a context, returning the matching elements as the seed set.
After obtaining the token list, context, and seed set, Sizzle compiles a nested function from all tokens. This “compile‑once‑execute‑many” approach uses closures so that repeated identical selectors can reuse the compiled function, dramatically improving performance.
The compilation creates two kinds of matcher functions:
1. For combinators (relationship selectors), it generates a function that combines the current selector with the previous one.
2. For non‑combinators, it directly checks whether each seed satisfies the selector.
For example, the selector
div > ayields two functions: one testing if the parent is a
div, another testing if the element itself is an
a. The combined matcher is applied to each seed.
The overall compilation steps are:
Check the cache for a previously compiled selector.
If not tokenized yet, run the tokenizer.
For each group, call
matcherFromTokensto obtain a matcher; pseudo‑classes go into
setMatchers, others into
elementMatchers.
Finally, combine
setMatchersand
elementMatchersvia
matcherFromGroupMatchers.
matcherFromTokensconverts a token array into a match function, handling both non‑combinator and combinator tokens. Non‑combinator tokens push their filter functions directly into a
matchersarray, while combinator tokens combine with the previous selector’s filter.
The
elementMatcherutility turns an array of filter functions into a single function that runs each filter sequentially.
The
Expr.filterobject contains specific filter functions for different selector types such as ID, CLASS, TAG, ATTR, CHILD, and PSEUDO. Each returns a function that checks a DOM element against the corresponding criterion.
<code>Expr.filter["ID"] = function(id) {
return function(elem) { return elem.id === id; };
};
Expr.filter["TAG"] = function(tag) {
return function(elem) { return elem.nodeName.toLowerCase() === tag; };
};</code>In summary, the Sizzle engine processes a selector by tokenizing it, narrowing the context and seed set, compiling a series of matcher functions, and finally filtering the seed elements through these matchers. The following optimization tips arise from this design:
Start selectors with an ID to quickly narrow the root node.
Place tag names before class names because
getElementsByTagNameis fast.
Avoid the universal selector
*; specify a concrete seed element.
Prefer parent‑child (>) combinators over descendant (space) selectors to reduce search scope.
Cache compiled jQuery objects to trade space for time.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.