Rychlost zpracování změn CSS

CSS styly musejí reagovat na celou řadu změn ve stránce, ať už je to akce uživatele (:hover či :focus) nebo programové změny (class či přidání DOM prvků). A zatímco některé změny jsou pro prohlížeč hračkou (např. změna barvy rámečku), tak nad některými musí strávit dlouhý čas, aby je do stránky přidal (např. změna šířky rámečku z 1px na 2px).

Tři úrovně změny

Při každé změně CSS stylu spadá změna každé vlastnosti do jedné kategorie podle toho, co ve stránce změní: rozvržení, uspořádání a vykreslení. (Přičemž změna rozvržení změní i uspořádání a změna uspořádání (většinou) vynutí překreslení).

A zatímco změny ve vykreslení a uspořádání znamenají pro prohlížeč jen překreslení změněné části stránky, změna v rozvržení pro něj znamená přepočítat celý layout stránky, znovu ho sestavit (reflow) a vykreslit.

Při změně stylu byste tedy vždy měli uvažovat nad tím, jak potřebná je daná změna, co ve stránce ovlivní a na jak dlouho to prohlížeč zaměstná. Jak už jsem psal v úvodu, změnit barvu nebo vzhled existujícího rámečku je mnohem rychlejší, než přidávat nový rámeček nebo měnit jeho šířku.

Zvláště důležité je to u animací, které změnu vyvolávají dlouhodobě a/nebo opakovaně a přepočítávat několikrát celou stránku jen proto, že chcete prvkem (třeba nevalidním inputem) zatřást ze strany na stranu je nesmysl. Pokud animaci nahradíte například probliknutím barvy rámečku a změnou ze solid na dashed, ušetříte spoustu času (a také baterie mobilních zařízení).

Rozvržení stránky (page layout)

Rozvržení stránky, tedy to jak jsou prvky umístěny a jak jsou velké, ovlivňují vlastnosti jako display, width, height, margin, border-width, box-sizing, font-size,  a prostě vše, co může daný prvek po stránce posunout a nebo změnit jeho velikost (za předpokladu, že má position: static).

Pokud například změníte o 1px výšku nadpisu stránka, nemůže prohlížeč jen posunout stránku o 1px (nahoru nebo dolu), ale musí přepočítat celý její obsah – vypočítat znovu rozměry všech prvků, znovu je do stránky umístit a znova vykreslit.

Vlastnosti jako padding (při box-sizing: border-box), overflow nebo align-items také mění rozvržení stránky, ale jen uvnitř daného prvku, takže při jejich změně nemusí prohlížeč znova přepočítávat celou stránku ale jen obsah daného kontejneru. Pokud je tím kontejnerem třeba jen obal (wrapper) obrázku, bude změna rychlá (překreslí se obrázek). Pokud ale změníte overflow na BODY, bude muset prohlížeč opět překreslit celou stránku.

I další vlastnosti mohou měnit velikost prvků, například content mění velikost podle zobrazeného textu nebo quotes mění velikost protože změní znaky před a za textem, white-space nebo text-overflow mění, jak je text v prvku zobrazen, atd.

Pokud chcete pro příklad přidat nevalidnímu prvku červený rámeček, který předtím neměl, změníte tím rozložení stránky a prohlížeč ji bude muset přepočítat. Pokud ale ve výchozím stylu nastavíte prvku průhledný okraj a pak jen změníte jeho barvu na červenou, kroky Rozložení a Uspořádání se tím přeskočí:

//Tohle je špatný přístup z několika důvodů
input {
    border: 0 none;
}
input:invalid {
    border: 1px solid red;
}

//Tohle je mnohem lepší
input {
    border: 1px solid transparent;
}
input:invalid {
    border-color: red;
}

Uspořádání stránky (page composition)

Uspořádání stránky znamená rozložení prvků ve třetím rozměru, tedy to, jak jsou prvky poskládány na sebe a který je vidět „na vrchu“. Ve výchozím rozložení se prvky nepřekrývají, ale pokud použijete position s hodnotami relative, absolute nebo fixed (a pak měníte související top, left, atd.) nebo třeba nastavíte overflow: visible a text vyteče z prvku, mohou se prvky začít překrývat a prohlížeč tedy musí zjistit, který má vykreslit (ten na vrchu). Další komplikací v uspořádání je průhlednost, ať už vynucená (opacity: 0.5), přirozená (color: rgba(0,0,0,0.5)) nebo výchozí (většina prvků má průhledné pozadí), protože pak musí prohlížeč vykreslit více prvků přes sebe a spočítat prolnutí barev.

Poznámka: Definice background: transparent nastavuje barvu pozadí na průhlednou a odpovídá tedy background-color: rgba(0,0,0,0), zatímco background: none ruší obrázek na pozadí a tudíž odpovídá background-image: none. Nicméně použití background nastaví všechny neuvedené hodnoty na výchozí, takže background: none má stejný účinek jako background: transparent.

Ve výchozím stavu jsou prvky HTML skládány na sebe tak, jak jsou uvedeny v HTML kódu, ale změnit to můžete například CSS vlastností z-index. Nepřímo může uspořádání ovlivnit i vlastnosti visibility: hidden, která prvek sice do stránky umístí, ale zabrání jeho vykreslení, takže efektivně bude „na vrchu“ prkvek pod ním.

Další vlastnosti, které mohou nepřímo změnit uspořádání a vést k nutnosti překreslit stránku jsou border-radius (mění viditelnost prvků pod okrajem), box-shadow (mění prolnutí do prvků níže) a filter (mění viditelnost prvku).

V případě, že používáte position: fixed, background-attachment: fixed nebo pespektivu, vyvolává změnu uspořádání i posun stránky (scroll), protože část stránky se posouvá a jiná zůstává na místě (nebo se posouvají jinou rychlostí) a tak není možné prostě jen pošoupnout již vykreslené pixely, ale je potřeba je znova přepočítat.

Důležité je si uvědomit, že změnou z-index neměníte rozměry ani umístění prvků (v osách X a Y) a tudíž je prohlížeč nemusí přepočítávat (vyhne se tedy kroku změna rozvržení).

Pokud například chcete na stránce prohazovat dva prvky (např. tlačítko Refresh a ikonu Loading), zkuste místo display: none a display: block (které mění rozložení stránky) udělat prvky neprůhledné a měnit jen jejich z-index, opacity nebo visibility, aby prohlížeč jen přepočítal jejich uspořádání a překreslil je.

Moderní prohlížeče navíc používají 3D rendering grafických karet, takže sami neurčují, který prvek je „na vrchu“, ale vykreslí všechny do různých vrstev (layers), přidají k nim informaci o jejich pozici v ose Z a grafická karta sama se pak postará o správné vykreslení (ne)průhledných částí. V takovém případě změna z-index jednoho prvku nepředstavuje pro prohlížeč žádnou zátěž, protože není potřeba nic překreslovat a jen se grafické kartě pošle informace o posunu prvku v 3D prostoru.

Vykreslení stránky (page paint)

Posledním typem změny stránky je její vykreslení, tedy fyzický převod HTML atributů a CSS vlastností na viditelné pixely.

I když by se z předchozího textu mohlo zdát, že vykreslení je tím poslední a nejjednodušším krokem, tak opak je pravdou a vykreslení zabírá většinu čas potřebného k aplikaci změn CSS.

Nicméně pořád je rozdíl v tom, zda je potřeba stránku pouze překreslit, nebo je potřeba ještě přepočítat rozměry a umístění všech prvků. Vždy se tedy snažte uvažovat nad tím, co daná změna CSS znamená a jak složité to pro prohlížeč bude.

Kromě výše uvedených změn rozložení a uspořádání vyvolává změnu vykreslení i všechny CSS pravidla měnící barvu (color, background-color, border-color, atd.) nebo jakýmkoliv jiným způsobem mění vnitřní vzhled prvku (outline, text-aligna většina text-* vlastností), background-image a související position, size, atd., vertical-align, overflow: auto/hidden/scroll, apod.).

Změny bez obětí

Samozřejmě existují i CSS vlastnosti, které nic samotný vzhled stránky neovlivní a není tak potřeba stránku překreslovat.

Například vlastnosti cursor a caret-color mění vzhled kurzorů myši a klávesnice, které nejsou součástí stránky a tak není potřeba nic překreslovat.

Vlastnost speak mění způsob přístupu asistenčních služeb text-to-speech (např. speak: spell-out) a neovlivňuje tedy grafický vzhled stránky (stejně jako color nezmění způsob, jak TTS text přečte).

Vlastnosti break-before/break-after a orphans/ widows, mění pouze vzhled při tisku a tak nemají vliv na zobrazení stránky v prohlížeči.

Rozsah změn

I výše uvedené typy změn nejsou úplně jednoznačné a prohlížeče mají různé optimalizace.

Pokud třeba změníte velikost prvku někde dole na stránce, nemusí prohlížeč přepočítávat všechny prvky od hlavičky až po patičku, ale obvykle přepočte velikost a umístění jen prvků v úrovni změněného prvku a všechny pod ním.

Stejně tak některé vlastnosti měnící uspořádání nebo vykreslení stránky mohou být méně náročné než jiné. Třeba změna barvy rámečku (border-color: red) bude rychlejší, než změna barvy pozadí (background: red), kde musí prohlížeč vypočítat prolnutí s textem, nebo změna obrázku na pozadí, kdy je potřeba obrázek správně zmenšit a vykreslit.

Containment neboli omezení změn

Containment je nová (leden 2020) CSS specifikace, která umožňuje autorům stránek pomocí CSS vlastnosti contain říci prohlížeči, jak se má zachovat při změně vzhledu nebo layoutu jednotlivých prvků a kontejnerů.

Důležité je uvědomit si, že jak specifikace tak i vlastnosti jsou pouze doporučení, takže prohlížeče mohou danou vlastnosti ignorovat (zcela nebo za určitých podmínek) nebo je mohou používat různě (např. přepočítat layout jen některých prvků a překreslit celou stránku nebo překreslit jen část stránky).

Vlastnost contain má tři hodnoty: style, paint a size (do budoucna se plánuje i hodnota style).

Layout

Nastavení contain: layout označí prvek jako samostatný blok, jehož obsah nemůže ovlivnit okolní prvky a naopak jeho obsah je nezávislý na změnách okolních prvků. Tedy kontejner má layout nezávislí na layoutu stránky. Při změně obsahu kontejneru tedy není potřeba přepočítávat layout stránky a obráceně při změně layout stránky nemusí být potřeba přepočítávat layout kontejneru.

Aby toho mohl prohlížeč dosáhnout, musí danému prvku vynutit některé vlastnosti, především position: relative, čímž vytvoří oddělený prostor pro pozicování (position a z-index). V případě, že některé vnitřní prvky přesahují rozměry kontejneru, prohlížeč daný kontejner zvětší, aby je mohl zobrazit, aniž by ovlivnil okolní prvky. Na vnořených prvcích nemusí správně fungovat záporný margin a jiné hodnoty, které by mohli ovlivnit prostor kolem kontejneru.

Existuje jedna důležitá výjimka: pokud změna vnějších nebo vnitřních prvků vynutí změnu velikosti kontejneru, který má nastaveno contain: layout, dojde k přepočtení layoutu celé stránky včetně vnořených prvků.

Paint

Hodnota contain: paint naopak prohlížeči říká, jak má prvek vykreslovat. Nastavení hodnoty znamená, že prostor kontejneru se bude vykreslovat nezávisle na okolních prvcích. Z toho vyplývá, že když se něco změní uvnitř kontejneru, prohlížeč překreslí pouze prostor kontejneru, ale nezmění zbytek stránky.

Aby toho mohl prohlížeč dosáhnout, musí kontejneru nastavit overflow: hidden a tudíž cokoliv, co přesahuje hranice kontejneru se nezobrazí. Dále se na kontejner aplikuje position: static, aby se prvky uvnitř pozicovaly v rámci kontejneru a neprolínaly se do okolních prvků.

Stejně jako u layout, pokud změna vnitřních nebo vnějších prvků vynutí změnu velikosti kontejneru, bude muset prohlížeč překreslit celou stránku včetně vnořených prvků.

Size

Hodnota contain: size říká prohlížeči, že daný prvek nikdy nezmění svoji velikost a není tudíž potřeba přepočítávat jeho layout při změně okolí nebo jeho vnořených prvků.

Vlastnost size má ale podmínku, že velikost kontejneru musí být nastavena! Pokud necháte výchozí velikost, za normálních podmínek se výška vypočte podle obsahu (délka textu, velikost obrázků, apod.); při použítí contain: size se ale výška nastaví na nulu, protože automatický výpočet výšky není kompatibilní s faktem, že se rozměr nesmí změnit.

I zde platí, že pokud se změní velikost kontejneru, bude muset prohlížeč přepočítat a překreslit celou stránku. Rozdíl je ale v tom, že v tomto případě ke změně velikosti kontejneru může dojít jen v případě, že přímo změníte jeho vlastnosti ovlivňující velikost (width, height, margin, atd.). Pokud je totiž nastaveno contain: size, nemůže změnu velikosti vyvolat změna vnitřních nebo vnějších prvků.

Content a Strict

Vlastnost contain může obsahovat více hodnot, například:

  • contain: layout size říká, že vnořené prvky ani samotný kontejner nemohou ovlivnit okolní layout,
  • contain: layout paint oddělí kontejner od stránky jak z hlediska layout tak i překreslení, ale zachová možnost automatické změny velikost kontejneru,
  • contain: layout paint size vytvoří skutečně zcela oddělený prostor, který nemůže ovlivnit své okolí (vyjma případu, kdy přímo změníte rozměr kontejneru, jak je uvedeno výše).

Abyste nemuseli dlouze psát často používané kombinace, existují aliasy: hodnota contain: content odpovídá contain: layout paint a hodnota contain: strict pak nastaví všechny tři hodnoty.

Podpora v prohlížečích

Containment je již implementován a podporován v nejnovějších verzích Firefox (69+) a Chrome (52+; Opera 40+) a také bude podporován v příští verzi Edge (76+). Nic vám tedy nebrání ho používat a ani není důvod to odkládat.

Jediný prohlížeč, který zatím podporu neoznámil, je Safari, což ale v dnešní době není nic překvapivého a uživatelé Maců a iPhonů musí počítat s tím, že nové vlastnosti přicházejí se zhruba 2 letým zpožděním oproti ostatním výrobcům. Samozřejmě IE11 také podporu nemá, což ale také není překvapivé.

Pokud máte obavy, že by uživatelé se staršími verzemi prohlížečů používali jiný layout, stačí společně s vlastností contain ručně nastavit i vlastnosti, které se tím vynutí (position a/nebo overflow). V případě contain: size pak musíte nastavit velikost kontejneru, která se bez problémů použije i ve starších prohlížečích a tudíž nehrozí odlišnosti v layoutu.

Předchozí odstavec platí pro nově vytvářené layouty. Pro úpravu existujících layoutů bude potřeba otestovat, zda změna neovlivní, jak daný layout funguje s novými vlastnostmi (např. že doplnění overflow: hidden neskryje některé důležité prvky). V některých případech (např. kontextové menu) může být potřeba layout upravit, aby se správně zobrazil (např. přesunout kontextové menu do jiného kontejneru, pokud má jeho rodič nastaveno contain: paint a menu z něj nemůže vytékat).

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..