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-align
a 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).