Optimize complex selector processing

Original author: Joshua Peek
  • Transfer
CSS selectors in the frontend can be compared to SQL statements in the backend. Despite their original role in CSS, we actively use selectors in our Javascript code, and given the declarative style of the selectors, they are priority candidates for optimization.

Browsers have many options for parsing, processing and matching a large number of CSS selectors. Modern web applications use thousands of selectors in their styles. To calculate the style for a single element, you have to consider a huge number of CSS rules. And browsers don't just go around all the selectors in a circle - it would be very slow.

Many browsers use a special grouping of data, discarding selectors that obviously will not work. In WebKit, this executes a RuleSet .

Selectorset


SelectorSet is a Javascript implementation of such a grouping. If you have a set of selectors known in advance - you can select elements by these selectors much more efficiently.

Selectors added to the set are analyzed and indexed by key. The key is formed from the significant right side of the selector. If the selector is aimed at id - this id becomes the key, if the class is on the right side, the class is used, etc. Then the selector is placed in a hash indexed by keys. Searching for keys in such a hash is pretty fast.

The idea is that when matching an element and a group of selectors, the properties of the element are analyzed for the possible presence of keys. Then these keys are searched in the hash, returning a smaller set of selectors, which already do a full search for matching elements and selectors.

How can we use this?

Speeding up delegated events


Probably the best known example of delegation is the jQuery function $.fn.live(new $.fn.on). The main advantage of using a delegated event handler instead of directly attached is that all new elements added after the event DOMContentLoadedwill also call this handler. Such an approach is often simply irreplaceable. For example, in a pattern like pjax , when the entire page is never overloaded.

But excessive delegation of events to document, such as $(‘.foo’).live(‘click’), or$(document).on(‘click’, ‘.foo’)- is fraught. The main problem is that the selector has to bypass the entire chain of ancestors of the element that caused the event. In large applications with large and complex DOMs, the nesting level can be 15 or higher. But this is not the saddest thing. The most interesting thing starts when there are a lot of delegated selectors. GitHub uses over 100, and Basecamp uses over 300 event handlers delegated to document.

But,


if you apply the above approach to grouping selectors, in the form of a patch for jQuery , you can significantly increase the processing of events in your application. Check out the difference in a small test that uses real markup and selectors with GitHub. In Chrome 32.0.1700, the increase in the processing speed of such events reaches 175% !

In fact, both of these libraries should be replaced by the browsers themselves. They, of course, already use their approaches to optimize CSS, but, sadly, they still do not have a native implementation of declarative event handlers, although some people have been using this since 2006 .

Links to used libraries
github.com/josh/selector-set
github.com/josh/jquery-selector-set

Also popular now: