Stínový DOM může obsahovat obě <style>
a <link rel="stylesheet" href="…">
značky. V druhém případě jsou šablony stylů uloženy v mezipaměti HTTP, takže se znovu nestahují pro více komponent, které používají stejnou šablonu.
Obecně platí, že místní styly fungují pouze uvnitř stínového stromu a styly dokumentů fungují mimo něj. Ale existuje několik výjimek.
:hostitel
:host
selector umožňuje vybrat stínového hostitele (prvek obsahující stínový strom).
Vyrábíme například <custom-dialog>
prvek, který by měl být vycentrován. K tomu potřebujeme nastylovat <custom-dialog>
samotný prvek.
Přesně to je :host
dělá:
<template id="tmpl">
<style>
/* the style will be applied from inside to the custom-dialog element */
:host {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: inline-block;
border: 1px solid red;
padding: 10px;
}
</style>
<slot></slot>
</template>
<script>
customElements.define('custom-dialog', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));
}
});
</script>
<custom-dialog>
Hello!
</custom-dialog>
Kaskádové
Stínový hostitel (<custom-dialog>
sám) se nachází v lehkém DOM, takže je ovlivněn pravidly CSS dokumentu.
Pokud existuje vlastnost ve stylu :host
lokálně a v dokumentu, pak má přednost styl dokumentu.
Pokud jsme například v dokumentu měli:
<style>
custom-dialog {
padding: 0;
}
</style>
…Poté <custom-dialog>
by bylo bez vycpávky.
Je to velmi pohodlné, protože v jeho :host
můžeme nastavit „výchozí“ styly komponent pravidlo a poté je v dokumentu snadno přepište.
Výjimkou je případ, kdy je místní vlastnost označena !important
, pro takové vlastnosti mají přednost místní styly.
:hostitel(selektor)
Stejné jako :host
, ale použije se pouze v případě, že stínový hostitel odpovídá selector
.
Například bychom chtěli vycentrovat <custom-dialog>
pouze pokud má centered
atribut:
<template id="tmpl">
<style>
:host([centered]) {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-color: blue;
}
:host {
display: inline-block;
border: 1px solid red;
padding: 10px;
}
</style>
<slot></slot>
</template>
<script>
customElements.define('custom-dialog', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));
}
});
</script>
<custom-dialog centered>
Centered!
</custom-dialog>
<custom-dialog>
Not centered.
</custom-dialog>
Nyní jsou další styly centrování aplikovány pouze na první dialog:<custom-dialog centered>
.
Abychom to shrnuli, můžeme použít :host
-rodina selektorů pro stylování hlavního prvku komponenty. Tyto styly (pokud není !important
) lze přepsat dokumentem.
Upravování stylů obsahu s bloky
Nyní se podívejme na situaci se sloty.
Drážkované prvky pocházejí z lehkého DOM, takže používají styly dokumentu. Místní styly neovlivňují obsah ve slotech.
V níže uvedeném příkladu s drážkou <span>
je tučné, podle stylu dokumentu, ale nebere background
z místního stylu:
<style>
span { font-weight: bold }
</style>
<user-card>
<div slot="username"><span>John Smith</span></div>
</user-card>
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
span { background: red; }
</style>
Name: <slot name="username"></slot>
`;
}
});
</script>
Výsledek je tučný, ale ne červený.
Pokud bychom chtěli v naší komponentě upravit styl štěrbinových prvků, máme dvě možnosti.
Nejprve můžeme upravit styl <slot>
a spoléhají na dědičnost CSS:
<user-card>
<div slot="username"><span>John Smith</span></div>
</user-card>
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
slot[name="username"] { font-weight: bold; }
</style>
Name: <slot name="username"></slot>
`;
}
});
</script>
Zde <p>John Smith</p>
se zobrazí tučně, protože dědičnost CSS je účinná mezi <slot>
a její obsah. Ale v CSS samotném nejsou všechny vlastnosti zděděny.
Další možností je použít ::slotted(selector)
pseudotřída. Přiřazuje prvky na základě dvou podmínek:
- To je štěrbinový prvek, který pochází z lehkého DOM. Název slotu nehraje roli. Prostě jakýkoli štěrbinový prvek, ale pouze prvek samotný, ne jeho potomci.
- Prvek odpovídá
selector
.
V našem příkladu ::slotted(div)
vybere přesně <div slot="username">
, ale ne jeho potomci:
<user-card>
<div slot="username">
<div>John Smith</div>
</div>
</user-card>
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
::slotted(div) { border: 1px solid red; }
</style>
Name: <slot name="username"></slot>
`;
}
});
</script>
Poznámka:::slotted
volič nemůže klesnout dále do slotu. Tyto selektory jsou neplatné:
::slotted(div span) {
/* our slotted <div> does not match this */
}
::slotted(div) p {
/* can't go inside light DOM */
}
Také ::slotted
lze použít pouze v CSS. Nemůžeme jej použít v querySelector
.
Háčky CSS s vlastními vlastnostmi
Jak stylizujeme vnitřní prvky komponenty z hlavního dokumentu?
Selektory jako :host
použít pravidla na <custom-dialog>
prvek nebo <user-card>
, ale jak v nich stylizovat stínové prvky DOM?
Neexistuje žádný selektor, který by mohl přímo ovlivnit stínové styly DOM z dokumentu. Ale stejně jako vystavujeme metody pro interakci s naší komponentou, můžeme vystavit proměnné CSS (vlastní vlastnosti CSS), abychom ji stylizovali.
Vlastní vlastnosti CSS existují na všech úrovních, ve světle i ve stínu.
Například ve stínovém DOM můžeme použít --user-card-field-color
Proměnná CSS pro pole stylu a vnější dokument může nastavit svou hodnotu:
<style>
.field {
color: var(--user-card-field-color, black);
/* if --user-card-field-color is not defined, use black color */
}
</style>
<div class="field">Name: <slot name="username"></slot></div>
<div class="field">Birthday: <slot name="birthday"></slot></div>
Poté můžeme tuto vlastnost deklarovat ve vnějším dokumentu pro <user-card>
:
user-card {
--user-card-field-color: green;
}
Vlastní vlastnosti CSS pronikají skrz stínový DOM, jsou viditelné všude, takže vnitřní .field
pravidlo jej využije.
Zde je úplný příklad:
<style>
user-card {
--user-card-field-color: green;
}
</style>
<template id="tmpl">
<style>
.field {
color: var(--user-card-field-color, black);
}
</style>
<div class="field">Name: <slot name="username"></slot></div>
<div class="field">Birthday: <slot name="birthday"></slot></div>
</template>
<script>
customElements.define('user-card', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode: 'open'});
this.shadowRoot.append(document.getElementById('tmpl').content.cloneNode(true));
}
});
</script>
<user-card>
<span slot="username">John Smith</span>
<span slot="birthday">01.01.2001</span>
</user-card>
Shrnutí
Shadow DOM může obsahovat styly, jako je <style>
nebo <link rel="stylesheet">
.
Místní styly mohou ovlivnit:
- strom stínů,
- stínový hostitel s
:host
a:host()
pseudotřídy, - slotové prvky (pocházející z lehkého DOM),
::slotted(selector)
umožňuje vybrat štěrbinové prvky samotné, ale ne jejich potomky.
Styly dokumentu mohou ovlivnit:
- stínový hostitel (ve vnějším dokumentu)
- prvky v štěrbinách a jejich obsah (stejně jako ve vnějším dokumentu)
Při konfliktu vlastností CSS mají obvykle přednost styly dokumentu, pokud vlastnost není označena jako !important
. Pak mají přednost místní styly.
Vlastní vlastnosti CSS pronikají skrz stínový DOM. Používají se jako „háky“ pro stylování komponenty:
- Komponenta používá vlastní vlastnost CSS ke stylování klíčových prvků, jako je
var(--component-name-title, <default value>)
. - Autor komponenty publikuje tyto vlastnosti pro vývojáře, jsou stejně důležité jako jiné metody veřejných komponent.
- Když chce vývojář upravit styl titulku, přiřadí
--component-name-title
Vlastnost CSS pro stínového hostitele nebo vyššího. - Zisk!