Choosing efficient selectors based on computational complexity - css

Given the CSS processing specifics, most notably RTL matching and selector efficiency, how should selectors be written purely from the perspective of rendering engine performance?
This should cover general aspects and include using or avoiding pseudo-classes, pseudo-elements and relationship selectors.

At runtime an HTML document is parsed into a DOM tree containing N elements with an average depth D. There is also a total of S CSS rules in the stylesheets applied.
Elements' styles are applied individually meaning there is a direct relationship between N and overall complexity. Worth noting, this can be somewhat offset by browser logic such as reference caching and recycling styles from identical elements. For instance, the following list items will have the same CSS properties applied (assuming no pseudo-classes such as :nth-child are applied):
<ul class="sample">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
Selectors are matched right-to-left for individual rule eligibility - i.e. if the right-most key does not match a particular element, there is no need to further process the selector and it is discarded. This means that the right-most key should match as few elements as possible. Below, the p descriptor will match more elements including paragraphs outside of target container (which, of course, will not have the rule apply but will still result in more iterations of eligibility checking for that particular selector):
.custom-container p {}
.container .custom-paragraph {}
Relationship selectors: descendant selector requires for up to D elements to be iterated over. For instance, successfully matching .container .content may only require one step should the elements be in a parent-child relationship, but the DOM tree will need to be traversed all the way up to html before an element can be confirmed a mismatch and the rule safely discarded. This applies to chained descendant selectors as well, with some allowances.
On the other hand, a > child selector, an + adjacent selector or :first-child still require an additional element to be evaluated but only have an implied depth of one and will never require further tree traversal.
The behavior definition of pseudo-elements such as :before and :after implies they are not part of the RTL paradigm. The logic the assumption is that there is no pseudo element per se until a rule instructs for it to be inserted before or after an element's content (which in turn requires extra DOM manipulation but there is no additional computation required to match the selector itself).
I couldn't find any information on pseudo-classes such as :nth-child() or :disabled. Verifying an element state would require additional computation, but from the rule parsing perspective it would only make sense for them to be excluded from RTL processing.
Given these relationships, computational complexity O(N*D*S) should be lowered primarily by minimizing the depth of CSS selectors and addressing point 2 above. This will result in quantifiably stronger improvements when compared to minimizing the number of CSS rules or HTML elements alone^
Shallow, preferably one-level, specific selectors are processed faster. This is taken to a whole new level by Google (programmatically, not by hand!), for instance there is rarely a three-key selector and most of the rules in search results look like
#gb {}
#gbz, #gbg {}
#gbz {}
#gbg {}
#gbs {}
.gbto #gbs {}
#gbx3, #gbx4 {}
#gbx3 {}
#gbx4 {}
/*...*/
^ - while this is true from a rendering engine performance standpoint, there are always additional factors such as traffic overhead and DOM parsing etc.
Sources: 1 2 3 4 5

Related

What is the difference between E:dir(dir) and E[dir="dir"] in CSS? [duplicate]

This question already has answers here:
What's the difference between html[lang="en"] and html:lang(en) in CSS?
(4 answers)
Closed 6 years ago.
W3C is introducing a new pseudo-class for direction detection in Selectors 4. I am wondering what is the difference between that and a normal attribute selector:
CSS2 - attribute selector
E[dir="rtl"] { ... }
Selectors 4 - dir pseudo-class
E:dir(rtl) { ... }
Is there a specific reason for creating a new pseudo-class for that? Are these selectors identical or do they behave differently? Are there any performance or specificity implications?
Is there a specific reason for creating a new pseudo-class for that?
The same reason the :lang() pseudo-class was introduced alongside attribute selectors in CSS2.1 See What's the difference between html[lang="en"] and html:lang(en) in CSS?
Are these selectors identical or do they behave differently?
See my answer to the linked question. Here's the relevant quotation from Selectors 4 for the sake of completeness:
The difference between :dir(C) and ''[dir=C]'' is that ''[dir=C]'' only performs a comparison against a given attribute on the element, while the :dir(C) pseudo-class uses the UAs knowledge of the document’s semantics to perform the comparison. For example, in HTML, the directionality of an element inherits so that a child without a dir attribute will have the same directionality as its closest ancestor with a valid dir attribute. As another example, in HTML, an element that matches ''[dir=auto]'' will match either :dir(ltr) or :dir(rtl) depending on the resolved directionality of the elements as determined by its contents. [HTML5]
To drive the point home on the similarities between :dir() and :lang(), if you look closely the first sentence is in fact a word-for-word copy of the same paragraph in the section describing :lang().
Much of the rest of the text for :lang() is new, however, because along with :dir(), Selectors 4 also introduces enhanced functionality for :lang().
Are there any performance or specificity implications?
Since the answer to your previous question is that they behave differently, performance is irrelevant.
No specificity implications because pseudo-classes and attribute selectors are equally specific.
1 It's not clear to me why it took almost 15 years for :dir() to be added to Selectors, but there you go.
According to MDN, some subtle differences exist:
The :dir CSS pseudo-class matches elements based on the directionality
of the text contained in it. In HTML, the direction is determined by
the dir attribute. For other document types there may be other
document methods for determining the language.
Note that the usage of the pseudo-class :dir() is not equivalent of
using the [dir=…] attribute selectors. The latter matches a value of
the dir and doesn't match when no attribute is set, even if in that
case the element inherits the value of its parent; similarly [dir=rtl]
or [dir=ltr] won't match the auto value that can be used on the dir
attribute. In the opposite, :dir() will match the value calculated by
the UA, being inherited or the auto value.
Also :dir() considers only the semantic value of the directionality,
the one defined in the document, most of the time in HTML. It won't
consider styling directionality, the one set by CSS properties like
direction which are purely stylistic.

CSS selectors, tagname + class vs class

Lets imagine very simple case:
div.className{}
vs
.className{}
Which option is faster and why ?
.className{} is faster in downloading, because of smaller size of the css file.
It is also faster when rendering page, because it is not necessary to look for div elements.
A guideline from google:
Avoid qualifying ID and class names with type selectors. Unless
necessary (for example with helper classes), do not use element names
in conjunction with IDs or classes.
Avoiding unnecessary ancestor selectors is useful for performance reasons .
One very important difference between div.classname and simply .classname is in something called selector specificity. It is a set of rules which defines which selector gets more weight once the browser starts going through all the selectors that potentially have influence on a particular element.
ne of the most basic rules of CSS is that selectors can be redefined in a way that whatever definition comes last and has influence on a particular element its the one that is going to be used (the sole exception being when using !important which always takes precedence).
Now in the above example redefining the .class selector should actually hide the text but instead its still visible. How is that possible if we said that latter rules always take precedence? Its because the div.classname rule has a higher specificity that .box since it actually gets points for containing both an element (div) and a class selector (.class) in its selector declaration (div.class).
Of course the div.class rule will be applied only on a div element but since class selectors are often reusable pieces of code there is plenty of situations when they are used on divs.
Although the rules in the official W3 specification are not that hard to understand they are sometimes pretty hard to remember. That's why I would like to recommend an excellent article on CSS selector specificity which can be found here.
In my opinion selector specificity is by far the most important thing to master when it comes to tracing inheritance problems with CSS stylesheets.
For Example
Find some more info
Follow Up

CSS Performance between class and attribute selectors

I'm wondering if there is a performance issue using attribute selectors instead of class selectors.
div.test {}
div[test] {}
P.S. : I'm only interested in CSS performance, not JS.
According to this article, class selectors are more efficient than attribute selectors.
Someone has actually created a real life selector test that showcases selector matching performance.
The table outlines that attribute selectors are approximately 3x slower compared to standard classes.
Relying on attribute selectors also requires more characters to target an element. It's better to define short and concise class names to keep your stylesheet small.
Example:
// 17 Characters (attribute)
[title="example"] {
...
}
// 6 Characters (class)
.title {
...
}
http://scope.bitbucket.org/tests/selector-matching-performance/
According to this project there is no real performance issue.
This surprised us, since selector performance hasn't been a real
concern in our day-to-day work for a while. Front-end performance is a
huge deal, of course, but CSS selectors appear to contribute such a
minuscule amount to the total page load time that it didn't occur to
us that it might be a major concern to many people.
They have benchmarks as well.
EDIT: see comment: they have benchmark ideas*, not benchmarks.
https://github.com/amcss/am-benchmarks
There is no performance issue. Both act same. But there is difference in specificity of the css with class versus Elements.
Specificity - Specificity determines, which CSS rule is applied by browsers.
If two selectors apply to the same element, the one with higher specificity wins.
But specificity has hierarchy.
Inline styles (Presence of style in document).
An inline style lives within your XHTML document. It is attached directly to the element to be styled. E.g.
IDs (# of ID selectors)
ID is an identifier for your page elements, such as #div.
Classes, attributes and pseudo-classes (# of class selectors).
This group includes .classes, [attributes] and pseudo-classes such as :hover, :focus etc.
Elements and pseudo-elements (# of Element (type) selectors).
Including for instance :before and :after.
Source: http://coding.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/
Hence div.test {} is more specific.

Modernizr and descendants selector?

According to mdn :
The descendant selector is the most expensive selector in CSS. It is
dreadfully expensive—especially if the selector is in the Tag or
Universal Category.
When Modernizr is up , it adds all the non supported classes to the html tag.
Meaning he can later do :
.myNotSupportedClass .myLastDiv <-- notice descendants selecotr[ ]
{
color:red;
}
But this is absolutely slow operation an operation which can be optimized.... which has to go through all the DOM tree in order to find the div.
I know there isn't another way of doing it , but still :
1) they could have added those classes to the body/form which is closer to the elements. soo there will be less of searching.
Or am I wrong ...,?
But this is absolutely slow operation
Eh.
they could have added those classes to the body/form which is closer to the elements. soo there will be less of searching.
body: how much less searching? 1 level less? How much of a difference does that make?
form: not every page has a form, and pages can have more than one form. The form element itself can be placed anywhere in the page body too, so the elements that should be affected by selectors in Modernizr class context may very well be unrelated to it, making searching completely impossible.
Do whatever you like, but since Modernizr chooses to place classes on the html element, write contextual selectors that make use of those classes and the descendant selector. If you're that obsessive about descendant selector performance, you have the choice not to use Modernizr and lose all the feature detection goodness it brings.
Why it chooses to place classes on the html element is anybody's guess. It could have been the most convenient element to place them on.

Correct terms and words for sections and parts of selectors

What is the correct term for the sections of CSS selectors that are separated by commas?
body.foo .login , body.bar .login { ... }
/* |
Part 1 | Part 2 */
Within those sections, what is the term for the parts separated by combinators (spaces, +, >, etc)?
body.foo .login , ... { ... }
/* |
Part 1 | Part 2 */
What is the correct term for the sections of CSS selectors that are separated by commas?
These are called complex selectors. The entire comma-separated list is known as a selector-list.
Within those sections, what is the term for the parts separated by combinators (spaces, +, >, etc)?
These are known as compound selectors.
So, a selector-list is made up of one or more complex selectors separated by commas, and each complex selector is made up of two main parts: compound selectors, and combinators. It can also optionally contain pseudo-elements.
Compound selectors used to have the rather convoluted name "sequence of simple selectors". Worse still, complex selectors were just called "selectors". Needless to say, I recommend using the new terms as they are much more straightforward, much less cumbersome and completely unambiguous compared to their predecessors.
And since I'm here, here are the rest of the definitions...
A simple selector is one fundamental component of selectors. It is any one of the following:
Universal selector (*), optionally with a namespace
Type selector (a, div, span, ul, li, etc), optionally with a namespace
Attribute selector ([att], [att=val], etc), optionally with a namespace
Class selector (.class)
ID selector (#id)
Pseudo-class (:pseudo-class)
As answered above, a compound selector (formerly a "sequence of simple selectors") is a chain of simple selectors not separated by a combinator:
a:not([rel="external"]):hover
A combinator is another fundamental component of selectors. It is a symbol or token that separates two compound selectors, establishing in its place a relationship between the two elements represented by the two compound selectors. There are currently four combinators in use today:
Descendant combinator:
article p
Child combinator:
/*
* The extra spaces in between are whitespace,
* and are therefore insignificant.
*/
ul > li
Adjacent sibling combinator:
header + section
General sibling combinator:
h2 ~ p
More combinators may be introduced in later specifications.
And a complex selector (formerly just "selector") is a complete string made up of compound selectors linked by combinators:
nav[role="main"] > ul:first-child > li
The subject of a complex selector is its last, or only, compound selector, representing the element that will be matched or styled. In the above example, the subject of the selector is li.
The term selector has been generalized, so it may now refer to any of the following for the purposes of simplicity and brevity, and which one it's referring to at any given moment should be gleaned from context:
Simple selector
Compound selector
Complex selector
Selector-list (e.g. the "selector" component of a style rule)
Some personal notes:
The term "key selector" was coined by browser vendors for use with selector implementations, and is not an official term. It is often used to mean "subject of the selector" however, because implementations happen to use the subject of the selector as the key for the purposes of determining matches.
The term "pseudo-selector" was coined by authors to mix pseudo-classes and pseudo-elements, and is not an official, or indeed meaningful, term. Although you can find it in some early-stage W3C CSS2/3 drafts, that was probably a mistake. Please don't use this term, as it needlessly creates confusion by attempting to group two completely different concepts into a single umbrella term.
Pseudo-elements (::pseudo-element) are not simple selectors, and therefore cannot appear in places where only actual elements may be matched. However, they are still considered selectors for the purposes of CSS parsing, and as stated above currently can appear at the end of any complex selector in a list (i.e. at the end of the last, or only, compound selector of each complex selector).
In CSS, a typical style rule (formerly "ruleset") consists of a selector and a declaration block.
Namespace prefixes are not selectors in their own right, but they may be applied to type selectors, universal selectors and attribute selectors to match components in a document that are (or are not) namespaced.
The specificity of a selector currently only refers to that of a single complex selector. When matching rules, any of the complex selectors in a list that match a given element will be considered for specificity calculations. If more than one complex selector matches an element, the most specific one will be used for calculations.
Specificity will be a more complicated issue with some level 4 selectors, such as :is() and the enhanced :not(), and the of S notation in the enhanced :nth-child() pseudo.
The specification offers terminology for this:
A selector is a chain of one or more sequences of simple selectors separated by combinators. One pseudo-element may be appended to the last sequence of simple selectors in a selector.
A sequence of simple selectors is a chain of simple selectors that are not separated by a combinator. It always begins with a type selector or a universal selector. No other type selector or universal selector is allowed in the sequence.
A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.
Combinators are: whitespace, "greater-than sign" (U+003E, >), "plus sign" (U+002B, +) and "tilde" (U+007E, ~). White space may appear between a combinator and the simple selectors around it. Only the characters "space" (U+0020), "tab" (U+0009), "line feed" (U+000A), "carriage return" (U+000D), and "form feed" (U+000C) can occur in whitespace. Other space-like characters, such as "em-space" (U+2003) and "ideographic space" (U+3000), are never part of whitespace.
There are some small terminology differences between CSS 2 and 3
the list of basic definitions (selector, group of selectors, simple selector, etc.) has been changed; in particular, what was referred to in CSS2 as a simple selector is now called a sequence of simple selectors, and the term "simple selector" is now used for the components of this sequence
The parts separated by commas are called selectors.
Within a selector we have simple_selectors and combinators.
See the grammar.

Resources