refactor 3-more into separate books

This commit is contained in:
Ilya Kantor 2015-02-27 13:21:58 +03:00
parent bd1d5e4305
commit 87639b2740
423 changed files with 9 additions and 9 deletions

View file

@ -0,0 +1,291 @@
# Стили и селекторы
Стилизация Shadow DOM покрывается более общей спецификацией ["CSS Scoping"](http://drafts.csswg.org/css-scoping/).
По умолчанию стили внутри Shadow DOM относятся только к его содержимому.
[cut]
Например:
```html
<!--+ run autorun="no-epub" -->
<p>Жили мы тихо-мирно, и тут...</p>
<p id="elem">Доброе утро, страна!</p>
<template id="tmpl">
*!*
<style> p { color: red; } </style>
*/!*
<h3><content></content></h3>
<p>Привет из подполья!</p>
</template>
<script>
var root = elem.createShadowRoot();
root.appendChild( tmpl.content.cloneNode(true) );
</script>
```
При запуске окрашенным в красный цвет окажется только `<p>` внутри Shadow DOM. Обратим внимание, окрасился именно тот элемент, который находится непосредственно в Shadow DOM. А элементы, которые отображены в Shadow DOM при помощи `<content>`, этот стиль не получили -- у них есть свои, заданные на внешней странице.
## Внешний стиль для Shadow DOM
Граница между Shadow DOM и основным DOM, хоть и существует, но при помощи специальных селекторов её можно переходить.
Если нужно с основной страницы стилизовать или выбрать элементы внутри Shadow DOM, то можно использовать селекторы:
<ul>
<li>**`::shadow` -- выбирает корень Shadow DOM.**
Выбранный элемент сам по себе не создаёт CSS box, но служит отправной точкой для дальшейшей выборки уже внутри дерева Shadow DOM.
Например, `#elem::shadow > div` найдёт внутри Shadow DOM `#elem` элементы `div` первого уровня.</li>
<li>**`>>>` -- особого вида CSS-селектор для всех элементов Shadow DOM, который полностью игнорирует границы между DOM'ами, включая вложенные подэлементы, у которых тоже может быть свой Shadow DOM.**
Например, `#elem >>> span` найдёт все `span` внутри Shadow DOM `#elem`, но кроме того, если в `#elem` есть подэлементы, у которых свой Shadow DOM, то оно продолжит поиск в них.
Вот пример, когда внутри одного Shadow DOM есть `<input type="date">`, у которого тоже есть Shadow DOM:
```html
<!--+ run -->
<style>
#elem::shadow span {
/* для span только внутри Shadow DOM #elem */
border-bottom: 1px dashed blue;
}
#elem >>> * {
/* для всех элементов внутри Shadow DOM #elem и далее внутри input[type=date] */
color: red;
}
</style>
<p id="elem"></p>
<script>
var root = elem.createShadowRoot();
root.innerHTML = "<span>Текущее время:</span> <input type='date'>";
</script>
```
</li>
<li>Кроме того, на Shadow DOM действует обычное CSS-наследование, если свойство поддерживает его по умолчанию.
В этом примере CSS-стили для `body` наследуются на внутренние элементы, включая Shadow DOM:
```html
<!--+ run autorun="no-epub" -->
<style>
body {
color: red;
font-style: italic;
}
</style>
<p id="elem"></p>
<script>
elem.createShadowRoot().innerHTML = "<span>Привет, мир!</span>";
</script>
```
Внутренний элемент станет красным курсивом.
</li>
</ul>
[warn header="Нельзя получить содержимое встроенных элементов"]
Описанные CSS-селекторы можно использовать не только в CSS, но и в `querySelector`.
Исключением являются встроенные элементы типа `<input type="date">`, для которых CSS-селекторы работают, но получить их содержимое нельзя.
Например:
```html
<!--+ run -->
<p id="elem"></p>
<script>
var root = elem.createShadowRoot();
root.innerHTML = "<span>Текущее время:</span> <input type='date'>";
// выберет только span из #elem
// вообще-то, должен выбрать span и из вложенных Shadow DOM,
// но для встроенных элементов - не умеет
alert(document.querySelectorAll('#elem::shadow span').length); // 1
</script>
```
[/warn]
## Стиль в зависимости от хозяина
Следующие селекторы позволяют изнутри Shadow DOM выбрать внешний элемент ("элемент-хозяин"):
<ul>
<li>`:host` выбирает элемент-хозяин, в котором, живёт Shadow DOM.
Хозяин :host выбирается в именно в контексте Shadow DOM.
То есть, это доступ не к внешнему элементу, а, скорее, к корню текущего Shadow DOM.
После `:host` мы можем указать селекторы и стили, которые нужно применить, если хозяин удовлетворяет тому или иному условию, например:
```html
<style>
:host > p { color: green; }
</style>
```
Этот селектор сработает для `<p>` первого уровня внутри Shadow DOM.
</li>
<li>`:host(селектор хозяина)` выбирает элемент-хозяин, если он подходит под селектор.
Этот селектор используется для темизации хозяина "изнутри", в зависимости от его классов и атрибутов. Он отлично добавляет просто `:host`, например:
```css
:host p { color: green; }
:host(.important) p { color: red; }
```
Здесь параграфы будут иметь `color:green`, но если у хозяина класс `.important`, то `color:red`.
</li>
<li>`:host-context(селектор хозяина)` выбирает элемент-хозяин, если какой-либо из его родителей удовлетворяет селектору, например:
```css
:host-context(h1) p {
/* селектор сработает для p, если хозяин находится внутри h1 */
}
```
Это используется для расширенной темизации, теперь уже не только в зависимости от его атрибутов, но и от того, внутри каких элементов он находится.
</li>
</ul>
Пример использования селектора `:host()` для разной расцветки Shadow DOM-сообщения, в зависимости от того, в каком оно `<p>`:
```html
<!--+ run autorun="no-epub" -->
*!*
<p class="message info">Доброе утро, страна!</p>
*/!*
*!*
<p class="message warning">Внимание-внимание! Говорит информбюро!</p>
*/!*
<template id="tmpl">
<style>
.content {
min-height: 20px;
padding: 19px;
margin-bottom: 20px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
}
*!*
:host(.info) .content {
color: green;
}
:host(.warning) .content {
color: red;
}
*/!*
</style>
<div class="content"><content></content></div>
</template>
<script>
var elems = document.querySelectorAll('p.message');
elems[0].createShadowRoot().appendChild( tmpl.content.cloneNode(true) );
elems[1].createShadowRoot().appendChild( tmpl.content.cloneNode(true) );
</script>
```
## Стиль для content
Тег `<content>` не меняет DOM, а указывает, что где показывать. Поэтому если элемент изначально находится в элементе-хозяине -- внешний документ сохраняет к нему доступ.
К нему будут применены стили и сработают селекторы, всё как обычно.
Например, здесь применится стиль для `<span>`:
```html
<!--+ run autorun="no-epub" -->
<style>
*!*
span { text-decoration: underline; }
*/!*
</style>
<p id="elem"><span>Доброе утро, страна!</span></p>
<template id="tmpl">
<h3><content></content></h3>
<p>Привет из подполья!</p>
</template>
<script>
elem.createShadowRoot().appendChild( tmpl.content.cloneNode(true) );
</script>
```
В примере выше заголовок "Доброе утро, страна!", который пришёл как `<span>` из внешнего документа, будет подчёркнут,
Итак, стили основного DOM-дерева применяются, всё в порядке.
Но что, если Shadow DOM тоже "имеет виды" на `<content>` и хочет стилизовать вставленное? Это тоже возможно.
**Для обращения к "содержимому" `<content>` из стилей внутри Shadow DOM используется псевдоэлемент `::content`.**
Например, изнутри Shadow DOM селектор `content[select="h1"]::content span` найдёт элемент `<content select="h1">` и его содержимом* отыщет `<span>`.
В примере ниже селектор `::content span` стилизует все `<span>` внутри всех `<content>`:
```html
<!--+ run -->
<style>
*!*
span { text-decoration: underline; }
*/!*
</style>
<p id="elem"><span>Доброе утро, страна!</span></p>
<template id="tmpl">
<style>
*!*
::content span { color: green; }
*/!*
</style>
<h3><content></content></h3>
<span>Привет из подполья!</span>
</template>
<script>
elem.createShadowRoot().appendChild( tmpl.content.cloneNode(true) );
</script>
```
Текст внутри `<h3>` -- зелёный и подчёркнутый одновременно, но стилизуется именно тот `<span>`, который показан в `<content>, а тот, который просто в Shadow DOM -- нет.
Приоритет селекторов расчитывается по [обычным правилам специфичности](http://www.w3.org/TR/css3-selectors/#specificity), если же приоритеты стилей на странице и в Shadow DOM и на странице равны, то, как описано в секции [Cascading](http://dev.w3.org/csswg/css-scoping/#cascading), побеждает страница, а для `!important`-стиля побеждает Shadow DOM.
## Итого
По умолчанию стили и селекторы из DOM-дерева действуют только на те элементы, в которых сами находятся.
Границу можно преодолевать, причём проще, естественно, от родителя к Shadow DOM, чем наоборот:
<ul>
<li>Снаружи можно выбирать и стилизовать элементы внутри Shadow DOM -- при помощи селекторов `::shadow` и `>>>`.</li>
<li>Изнутри Shadow DOM можно стилизовать не только то, что изначально в Shadow DOM, но и узлы, показываемые в `<content>`.</li>
<li>Также можно ставить стиль в зависимость от хозяиня при помощи селекторов `::host`, `::host-context`, но выбирать и стилизовать произвольные теги внутри хозяина нельзя.</li>
</ul>