Écriture de CSS efficace

  • Raccourci de la révision : Écriture_de_CSS_efficace
  • Titre de la révision : Écriture de CSS efficace
  • ID de la révision : 265923
  • Créé :
  • Créateur : rd6137
  • Version actuelle ? Non
  • Commentaire 193 words added, 177 words removed

Contenu de la révision

Ce document vous donne des indications pour optimiser votre code CSS, et plus particulièrement comment écrire des sélecteurs efficaces.

La spécification CSS ne précise pas comment les navigateurs doivent implémenter le système de style, mais le résultat final. De fait, de nombreux moteurs de styles peuvent avoir des comportements très différents en terme de performance, en particulier Gecko et WebKit, tous deux libres, utilisent des algorithmes similaires, avec des forces et des faiblesses similaires. Les astuces présentées ici seront donc utiles pour les documents réels du Web.

La première section traite de la manière habituelle dont les styles sont catégorisés. Basé sur cette connaissance, les sections suivantes contiennent des indications qui utilisent ce style de système d'implémentation.

Comment le système de style lis les règles

Les règles de style sont tout d'abord séparées en quatre catégories

  1. Règles d'identification
  2. Règles de classe
  3. Règles de balise
  4. Règles universelles

Il vous est nécessaire de comprendre ces catégories, car elles sont les blocs fondamentaux utilisés pour construire les règles d'attribution des blocks.

La clé de sélection, dans les paragraphes suivants, signifie l'extrémité du sélecteur (celle qui pointe vers l'élément souhaité, et non un de ses ancêtres).

Par exemple, dans la règle

a img, div > p, h1 + [title] {…} 

les clés de sélections img, p, et {{ mediawiki.external('title') }}.

Règles d'identification

La première catégorie de règles concerne les règles qui utilisent un champ ID comme clé de sélection.

Exemple
button#backButton {…} /* This is an ID-categorized rule */
#urlBar[type="autocomplete"] {…} /* This is an ID-categorized rule */
treeitem > treerow > treecell#myCell:active {…} /* This is an ID-categorized rule */ 

Règles de classe

Concerne les règles qui utilisent l'attribut class comme clé de sélection.

Exemple
button.toolbarButton {…} /* A class-based rule */
.fancyText {…}	/* A class-based rule */
menuitem > .menu-left[checked="true"] {…} /* A class-based rule */ 

Règles de balise

S'il la clé de sélection n'est ni class, ni ID, le système considère alors les balises. Si une règle utilise une balise comme clé de sélection, alors c'est ce type de règle qui est utilisé.

Exemple
td {…} /* A tag-based rule */
treeitem > treerow {…} /* A tag-based rule */
input[type="checkbox"] {…} /* A tag-based rule */ 

Règles universelles

Toutes les règles qui ne sont hors des catégories précédentes

Exemple
[hidden="true"] {…} /* A universal rule */  
* {…}		/* A universal rule */
tree > [collapsed="true"] {…} /* A universal rule */

How the style system matches rules

The style system matches rules by starting with the key selector, then moving to the left (looking for any ancestors in the rule’s selector). As long as the selector’s subtree continues to check out, the style system continues moving to the left until it either matches the rule, or abandons because of a mismatch.

The most fundamental concept to learn is this rule filtering. The categories exist in order to filter out irrelevant rules (so the style system doesn’t waste time trying to match them).

This is the key to dramatically increasing performance. The fewer rules required to check for a given element, the faster style resolution will be.

For example, if an element has an ID, then only ID rules that match the element’s ID will be checked. Only Class Rules for a class found on the element will be checked. Only Tag Rules that match the tag will be checked. Universal Rules will always be checked.

Guidelines for efficient CSS

Avoid universal rules

Make sure a rule doesn’t end up in the universal category!

Don’t qualify ID rules with tag names or classes

If a rule has an ID selector as its key selector, don’t add the tag name to the rule. Since IDs are unique, adding a tag name would slow down the matching process needlessly.

Exception: When it’s desirable to change the class of an element dynamically in order to apply different styles in different situations, but the same class is going to be shared with other elements.
BAD
button#backButton {…}
BAD
.menu-left#newMenuIcon {…}
GOOD
#backButton {…}
GOOD
#newMenuIcon {…}

Don’t qualify class rules with tag names

The previous concept also applies here.  All class names are unique.

One convention you can use is to include the tag name in the class name.  However, this may cost some flexibility; if design changes are made to the tag, the class names must be changed as well.  (It’s best to choose strictly semantic names, as such flexibility is one of the aims of separate stylesheets.)

BAD
treecell.indented {…}
GOOD
.treecell-indented {…}
BEST
.hierarchy-deep {…}

Use the most specific category possible

The single biggest cause of slowdown is too many rules in the tag category. By adding classes to our elements, we can further subdivide these rules into Class Categories, which eliminates time spent trying to match rules for a given tag.

BAD
treeitem{{ mediawiki.external('mailfolder=\"true\"') }} > treerow > treecell {…}
GOOD
.treecell-mailfolder {…}

Avoid the descendant selector

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.

Frequently, what is really desired is the child selector.  For instance, the performance are so bad, that descendant selectors are banned in Firefox' User Interface CSS, without a specific justification. You should do the same on your Web pages

BAD
treehead treerow treecell {…}
BETTER, BUT STILL BAD (see next guideline)
treehead > treerow > treecell {…}

Tag category rules should never contain a child selector

Avoid using the child selector with tag category rules. This will dramatically lengthen the match time (especially if the rule is likely to be matched) for all occurrences of that element.

BAD
treehead > treerow > treecell {…}
GOOD
.treecell-header {…}

Question all usages of the child selector

Exercise caution when using the child selector. Avoid it if you can.

In particular, the child selector is frequently used with RDF trees and menus like so:

BAD
treeitem{{ mediawiki.external('IsImapServer=\"true\"') }} > treerow > .tree-folderpane-icon {…}

Remember that REF attributes can be duplicated in a template! Take advantage of this.  Duplicate RDF properties on child XUL elements in order to change them based on the attribute.

GOOD
.tree-folderpane-icon{{ mediawiki.external('IsImapServer=\"true\"') }} {…}

Rely on inheritance

Learn which properties inherit, and allow them to do so!  

For example, XUL widgets are explicitly set up such that a parent’s list-style-image or font rules will filter down to anonymous content. It’s not necessary to waste time on rules that talk directly to anonymous content.

BAD
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
GOOD
#bookmarkMenuItem { list-style-image: url(blah) }

In the above example, the desire to style anonymous content (without leveraging the inheritance of list-style-image) resulted in a rule that was in the Class Category, when the rule should have ended up in the ID Category—the most specific category of all!

Remember: Elements all have the same classes—especially anonymous content!

The above “bad” rule forces every menu’s icons to be tested for containment within the bookmarks menu item. Since there are many menus, this is extraordinarily expensive.  Instead, the “good” rule limits the testing to the bookmarks menu.

Use -moz-image-region!

Putting a bunch of images into a single image file and selecting them with {{ Cssxref("-moz-image-region") }} performs significantly better than putting each image into its own file.

Use scoped stylesheets

If you specify a stylesheet as an XBL resource, the styles only apply to the bound elements and their anonymous content. This reduces the inefficiency of universal rules and child selectors because there are fewer elements to consider.

Source de la révision

<p>Ce document vous donne des indications pour optimiser votre code CSS, et plus particulièrement comment écrire des sélecteurs efficaces.</p>
<p>La spécification CSS ne précise pas comment les navigateurs doivent implémenter le système de style, mais le résultat final. De fait, de nombreux moteurs de styles peuvent avoir des comportements très différents en terme de performance, en particulier Gecko et WebKit, tous deux libres, utilisent des algorithmes similaires, avec des forces et des faiblesses similaires. Les astuces présentées ici seront donc utiles pour les documents réels du Web.</p>
<p>La première section traite de la manière habituelle dont les styles sont catégorisés. Basé sur cette connaissance, les sections suivantes contiennent des indications qui utilisent ce style de système d'implémentation.</p>
<h3 name="How_the_style_system_breaks_up_rules">Comment le système de style lis les règles</h3>
<p>Les règles de style sont tout d'abord séparées en quatre catégories</p>
<ol> <li>Règles d'identification</li> <li>Règles de classe</li> <li>Règles de balise</li> <li>Règles universelles</li>
</ol>
<p>Il vous est nécessaire de comprendre ces catégories, car elles sont les blocs fondamentaux utilisés pour construire les règles d'attribution des blocks.</p>
<p>La clé de sélection, dans les paragraphes suivants, signifie l'extrémité du sélecteur (celle qui pointe vers l'élément souhaité, et non un de ses ancêtres).</p>
<p>Par exemple, dans la règle</p>
<div style="width: 100%;"> <pre class="eval deki-transform"><code style="">a img, div &gt; p, h1 + [title] {…} </code>
</pre>
</div>
<p>les clés de sélections <code>img</code>, <code>p</code>, et <code>{{ mediawiki.external('title') }}</code>.</p>
<h4 name="ID_Rules">Règles d'identification</h4>
<p>La première catégorie de règles concerne les règles qui utilisent un champ <code>ID</code> comme clé de sélection.</p>
<h5>Exemple</h5>
<pre class="eval deki-transform"><code style="">button#backButton {…} /* This is an ID-categorized rule */
#urlBar[type="autocomplete"] {…} /* This is an ID-categorized rule */
treeitem &gt; treerow &gt; treecell#myCell:active {…} /* This is an ID-categorized rule */ </code>
</pre>
<h4 name="Class_Rules">Règles de classe</h4>
<p>Concerne les règles qui utilisent l'attribut <code>class </code>comme clé de sélection.</p>
<h5>Exemple</h5>
<pre class="eval deki-transform"><code style="">button.toolbarButton {…} /* A class-based rule */
.fancyText {…}	/* A class-based rule */
menuitem &gt; .menu-left[checked="true"] {…} /* A class-based rule */ </code>
</pre>
<h4 name="Tag_Rules">Règles de balise</h4>
<p>S'il la clé de sélection n'est ni <code>class</code>, ni <code>ID</code>, le système considère alors les balises. Si une règle utilise une balise comme clé de sélection, alors c'est ce type de règle qui est utilisé.</p>
<h5>Exemple</h5>
<pre class="eval deki-transform"><code style="">td {…} /* A tag-based rule */
treeitem &gt; treerow {…} /* A tag-based rule */
input[type="checkbox"] {…} /* A tag-based rule */ </code>
</pre>
<h4 name="Universal_Rules">Règles universelles</h4>
<p>Toutes les règles qui ne sont hors des catégories précédentes</p>
<h5>Exemple</h5>
<pre class="eval deki-transform"><code style="">[hidden="true"] {…} /* A universal rule */  
* {…}		/* A universal rule */
tree &gt; [collapsed="true"] {…} /* A universal rule */</code>
</pre>
<h3 name="How_the_Style_System_Matches_Rules">How the style system matches rules</h3>
<p>The style system matches rules by starting with the key selector, then moving to the left (looking for any ancestors in the rule’s selector). As long as the selector’s subtree continues to check out, the style system continues moving to the left until it either matches the rule, or abandons because of a mismatch.</p>
<p>The most fundamental concept to learn is this rule filtering. The categories exist in order to filter out irrelevant rules (so the style system doesn’t waste time trying to match them).</p>
<p><strong>This is the key to dramatically increasing performance.</strong> The fewer rules required to check for a given element, the faster style resolution will be.</p>
<p style="text-align: justify; ">For example, if an element has an ID, then only ID rules that match the element’s ID will be checked. Only Class Rules for a class found on the element will be checked. Only Tag Rules that match the tag will be checked. Universal Rules will always be checked.</p>
<h3 name="Guidelines_for_Efficient_CSS">Guidelines for efficient CSS</h3>
<h4 name="Avoid_Universal_Rules.21">Avoid universal rules</h4>
<p>Make sure a rule doesn’t end up in the universal category!</p>
<h4 name="Don.27t_qualify_ID-categorized_rules_with_tag_names_or_classes">Don’t qualify ID rules with tag names or classes</h4>
<p><strong>If a rule has an ID selector as its key selector, don’t add the tag name to the rule.</strong> Since IDs are unique, adding a tag name would slow down the matching process needlessly.</p>
<div class="note"><strong>Exception:</strong> When it’s desirable to change the <code>class</code> of an element dynamically in order to apply different styles in different situations, but the same <code>class</code> is going to be shared with other elements.</div>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>button#backButton {…}</code></dd> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>.menu-left#newMenuIcon {…}</code></dd> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>#backButton {…}</code></dd> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>#newMenuIcon {…}</code></dd>
</dl>
<h4 name="Don.27t_qualify_class-categorized_rules_with_tag_names">Don’t qualify class rules with tag names</h4>
<p>The previous concept also applies here.  All class names are unique.</p>
<p>One convention you can use is to include the tag name <em>in</em> the class name.  However, this may cost some flexibility; if design changes are made to the tag, the class names must be changed as well.  (It’s best to choose strictly semantic names, as such flexibility is one of the aims of separate stylesheets.)</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>treecell.indented {…}</code></dd> <dt><span style="color: rgb(153, 204, 0); ">GOOD</span></dt> <dd><code>.treecell-indented {…}</code></dd> <dt><span style="color: rgb(51, 153, 102); ">BEST</span></dt> <dd><code>.hierarchy-deep {…}</code></dd>
</dl>
<h4 name="Try_to_put_rules_into_the_most_specific_category_you_can.21">Use the most specific category possible</h4>
<p><strong>The single biggest cause of slowdown is too many rules in the tag category. </strong>By adding classes to our elements, we can further subdivide these rules into Class Categories, which eliminates time spent trying to match rules for a given tag.</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>treeitem{{ mediawiki.external('mailfolder=\"true\"') }} &gt; treerow &gt; treecell {…}</code></dd> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>.treecell-mailfolder {…}</code></dd>
</dl>
<h4 name="Avoid_the_descendant_selector.21">Avoid the descendant selector</h4>
<p><strong>The descendant selector is the most expensive selector in CSS.</strong> It is <em>dreadfully</em> expensive—especially if the selector is in the Tag or Universal Category.</p>
<p>Frequently, what is really desired is <strong>the child selector</strong>.  For instance, the performance are so bad, that descendant selectors are banned in Firefox' User Interface CSS, without a specific justification. You should do the same on your Web pages</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>treehead treerow treecell {…}</code></dd> <dt><span style="color: rgb(153, 51, 0); ">BETTER, BUT STILL BAD (see next guideline)</span></dt> <dd><code>treehead &gt; treerow &gt; treecell {…}</code></dd>
</dl>
<h4 name="Tag-categorized_rules_should_never_contain_a_child_selector.21">Tag category rules should <em>never</em> contain a child selector</h4>
<p><strong>Avoid using the child selector with tag category rules.</strong> This will dramatically lengthen the match time (especially if the rule is likely to be matched) for all occurrences of that element.</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>treehead &gt; treerow &gt; treecell {…}</code></dd> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>.treecell-header {…}</code></dd>
</dl>
<h4 name="Question_all_usages_of_the_child_selector.21">Question all usages of the child selector</h4>
<p><strong>Exercise caution when using the child selector.</strong> Avoid it if you can.</p>
<p>In particular, the child selector is frequently used with RDF trees and menus like so:</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>treeitem{{ mediawiki.external('IsImapServer=\"true\"') }} &gt; treerow &gt; .tree-folderpane-icon {…}</code></dd>
</dl>
<p>Remember that REF attributes can be duplicated in a template! Take advantage of this.  Duplicate RDF properties on child XUL elements in order to change them based on the attribute.</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>.tree-folderpane-icon{{ mediawiki.external('IsImapServer=\"true\"') }} {…}</code></dd>
</dl>
<h4 name="Rely_on_inheritance.21">Rely on inheritance</h4>
<p>Learn which properties inherit, and allow them to do so!  </p>
<p>For example, XUL widgets are explicitly set up such that a parent’s <code>list-style-image</code> or <code>font</code> rules will filter down to anonymous content. It’s not necessary to waste time on rules that talk directly to anonymous content.</p>
<dl style="margin: 1em 12.5%"> <dt><span style="color: rgb(128, 0, 0); ">BAD</span></dt> <dd><code>#bookmarkMenuItem &gt; .menu-left { list-style-image: url(blah) }</code></dd> <dt><span style="color: rgb(51, 153, 102); ">GOOD</span></dt> <dd><code>#bookmarkMenuItem { list-style-image: url(blah) }</code></dd>
</dl>
<p>In the above example, the desire to style anonymous content (without leveraging the inheritance of <code>list-style-image</code>) resulted in a rule that was in the Class Category, when the rule should have ended up in the ID Category—the most specific category of all!</p>
<p><strong>Remember:</strong> Elements all have the same classes—especially anonymous content!</p>
<p>The above “bad” rule forces every menu’s icons to be tested for containment within the bookmarks menu item. Since there are many menus, this is extraordinarily expensive.  Instead, the “good” rule limits the testing to the bookmarks menu.</p>
<h4 name="Use_-moz-image-region.21">Use <code>-moz-image-region</code>!</h4>
<p>Putting a bunch of images into a single image file and selecting them with <code>{{ Cssxref("-moz-image-region") }}</code> performs significantly better than putting each image into its own file.</p>
<h4>Use scoped stylesheets</h4>
<p>If you specify a stylesheet as an XBL resource, the styles only apply to the bound elements and their anonymous content. This reduces the inefficiency of universal rules and child selectors because there are fewer elements to consider.</p>
Revenir à cette révision