renovate webcomponents
This commit is contained in:
parent
35081a779a
commit
0e62abbff8
21 changed files with 421 additions and 476 deletions
|
@ -1,4 +1,4 @@
|
|||
# Shadow DOM, шаблоны и стили
|
||||
# Shadow DOM
|
||||
|
||||
Спецификация [Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/) является отдельным стандартом. Частично он уже используется для обычных DOM-элементов, но также применяется для создания веб-компонентов.
|
||||
|
||||
|
@ -44,7 +44,7 @@ Shadow DOM можно создать внутри любого элемента
|
|||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
<!--+ run autorun="no-epub" -->
|
||||
<p id="elem">Доброе утро, страна!</p>
|
||||
|
||||
<script>
|
||||
|
@ -60,7 +60,7 @@ Shadow DOM можно создать внутри любого элемента
|
|||
Внутрь этого Shadow DOM, при желании, можно поместить обычное содержимое. Для этого нужно указать, куда. В Shadow DOM это делается через "точку вставки" (insertion point). Она объявляется при помощи тега `<content>`, например:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
<!--+ run autorun="no-epub" -->
|
||||
<p id="elem">Доброе утро, страна!</p>
|
||||
|
||||
<script>
|
||||
|
@ -92,7 +92,7 @@ Shadow DOM примера выше в инструментах разработ
|
|||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
<!--+ run autorun="no-epub" -->
|
||||
|
||||
<section id="elem">
|
||||
<h1>Новости</h1>
|
||||
|
@ -129,7 +129,7 @@ Shadow DOM примера выше в инструментах разработ
|
|||
Если нужно работать с содержимым в Shadow DOM, то нужно перейти к нему через `elem.shadowRoot`. Можно и создать новое Shadow DOM-дерево из JavaScript, например:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
<!--+ run autorun="no-epub" -->
|
||||
<p id="elem">Доброе утро, страна!</p>
|
||||
|
||||
<script>
|
||||
|
@ -157,335 +157,6 @@ Shadow DOM примера выше в инструментах разработ
|
|||
На момент написания статьи `shadowRoot` можно получить только для Shadow DOM, созданного описанным выше способом, но не встроенного, как в элементах типа `<input type="date">`.
|
||||
[/warn]
|
||||
|
||||
## Шаблоны <template>
|
||||
|
||||
Элемент `<template>` предназначен для хранения "образца" разметки, невидимого и предназначенного для вставки куда-либо.
|
||||
|
||||
Конечно, есть много способов записать произвольный невидимый текст в HTML. В чём же особенность `<template>`?
|
||||
|
||||
Его отличие от обычных тегов в том, что его содержимое обрабатывается особым образом. Оно не только показывается, но и считается находящимся вообще "вне документа".
|
||||
|
||||
Однако, вместе с тем, оно всё же обрабатывается браузером (а значит должно быть корректным HTML) и записывается как `DocumentFragment` в свойство тега `content`. Предполагается, что мы, при необходимости, возьмём `content` и вставим, куда надо.
|
||||
|
||||
Пример вставки шаблона `tmpl` в Shadow DOM элемента `elem`:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
|
||||
<p id="elem">Доброе утро, страна!</p>
|
||||
|
||||
<template id="tmpl">
|
||||
<h3><content></content></h3>
|
||||
<p>Привет из подполья!</p>
|
||||
<script> document.write('...document.write:Новость!'); </script>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var root = elem.createShadowRoot();
|
||||
root.appendChild( tmpl.content.cloneNode(true) );
|
||||
</script>
|
||||
```
|
||||
|
||||
У нас получилось, что:
|
||||
<ol>
|
||||
<li>В элементе `#elem` содержатся данные в некоторой оговорённой разметке.</li>
|
||||
<li>Шаблон `#tmpl` указывает, как их отобразить, куда и в какие HTML-теги завернуть содержимое `#elem`.</li>
|
||||
<li>Это содержимое добавляется в Shadow DOM тега. Технически, шаблон можно использовать и без Shadow DOM, но тогда не сработает тег `<content>`.</li>
|
||||
</ol>
|
||||
|
||||
Важные детали:
|
||||
<ul>
|
||||
<li>В отличие от вставки через `innerHTML` и от обычного `DocumentFragment`, скрипт внутри шаблона выполнится при вставке. Содержимое шаблона изначально "вне документа" и "оживает", когда оно попадает в него. Это относится ко всему -- картинки начинают загружаться, видео -- проигрываться и т.п.</li>
|
||||
<li>Мы вставляем не сам `tmpl.content`, а его клон. Это обычная практика, чтобы можно было использовать один шаблон много раз.</li>
|
||||
</ul>
|
||||
|
||||
## Стили
|
||||
|
||||
Стилизация Shadow DOM покрывается более общей спецификацией ["CSS Scoping"](http://drafts.csswg.org/css-scoping/).
|
||||
|
||||
**По умолчанию стили внутри Shadow DOM относятся только к его содержимому.**
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
<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, то можно использовать селекторы:
|
||||
|
||||
<ul>
|
||||
<li>**`::shadow` -- выбирает корень Shadow DOM.**
|
||||
|
||||
Например, `#elem::shadow div` найдёт внутри Shadow DOM `#elem` элементы `div`.</li>
|
||||
<li>**`/deep/` -- особого вида CSS-селектор для всех элементов Shadow DOM, который полностью игнорирует границы между DOM'ами, включая вложенные подэлементы, у которых тоже может быть свой Shadow DOM.**
|
||||
|
||||
Например, `#elem /deep/ 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 */
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
##elem /deep/ span {
|
||||
/* для span внутри 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 -->
|
||||
<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.querySelector('#elem /deep/ span').length); // 1
|
||||
</script>
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
### Стиль Shadow DOM в зависимости от хозяина
|
||||
|
||||
Следующие селекторы позволяют выбрать элемент-хозяин:
|
||||
|
||||
<ul>
|
||||
<li>**`:host` выбирает элемент-хозяин**, в котором, живёт Shadow DOM.</li>
|
||||
<li>**`:host(селектор хозяина)` выбирает элемент-хозяин, если он подходит под селектор.**
|
||||
|
||||
Например:
|
||||
|
||||
```css
|
||||
:host(.important) {
|
||||
/* сработает, если хозяин имеет класс important */
|
||||
}
|
||||
```
|
||||
|
||||
Этот селектор используется для темизации хозяина "изнутри", в зависимости от его классов и атрибутов.
|
||||
|
||||
**Хозяин :host выбирается в именно в контексте Shadow DOM.**
|
||||
|
||||
То есть, это доступ не к внешнему элементу, а, скорее, к корню текущего Shadow DOM.
|
||||
|
||||
После `:host(...)` мы можем указать селекторы и стили, которые нужно применить, если хозяин удовлетворяет тому или иному условию, например:
|
||||
|
||||
```html
|
||||
<style>
|
||||
:host p { color: green; }
|
||||
:host(.important) p { color: red; }
|
||||
</style>
|
||||
```
|
||||
|
||||
Эти селекторы сработают для `<p>` внутри Shadow DOM, причём второй -- только если у хозяина стоит класс `important`.
|
||||
</li>
|
||||
<li>**`:host-context(селектор хозяина)` выбирает элемент-хозяин, если какой-либо из его родителей удовлетворяет селектору.**
|
||||
|
||||
Например:
|
||||
|
||||
```css
|
||||
:host-context(h1) p {
|
||||
/* селектор сработает для p, если хозяин находится внутри h1 */
|
||||
}
|
||||
```
|
||||
|
||||
Это используется для расширенной темизации, теперь уже не только в зависимости от его атрибутов, но и от того, внутри каких элементов он находится.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Пример использования селектора `:host()` для темизации содержимого:
|
||||
|
||||
```html
|
||||
<!--+ run autorun -->
|
||||
*!*
|
||||
<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 -->
|
||||
<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>` из внешнего документа, будет подчёркнут,
|
||||
|
||||
...Но, поскольку эти узлы показываются внутри Shadow DOM, то ему тоже может понадобится к ним доступ.
|
||||
|
||||
**Для обращения к "содержимому" `<content>` используется псевдоэлемент `::content`.**
|
||||
|
||||
Например, `content[select="h1"]::content span` найдёт элемент `<content select="h1">` и *в его содержимом* отыщет `<span>`.
|
||||
|
||||
Селектор `::content` подразумевает `*::content`, так что `::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>
|
||||
<p>Привет из подполья!</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
elem.createShadowRoot().appendChild( tmpl.content.cloneNode(true) );
|
||||
</script>
|
||||
```
|
||||
|
||||
Если запустить пример выше, то текст внутри `<h3>` станет зелёным и подчёркнутым одновременно.
|
||||
|
||||
Приоритет селекторов расчитывается по [обычным правилам специфичности](http://www.w3.org/TR/css3-selectors/#specificity), если же приоритеты стилей на странице и в Shadow DOM и на странице равны, то, как описано в секции [Cascading](http://dev.w3.org/csswg/css-scoping/#cascading), побеждает страница, а для `!important`-стиля побеждает Shadow DOM.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
[summary]
|
||||
Если обобщить -- инкапсуляция Shadow DOM имеет односторонний характер:
|
||||
<ul>
|
||||
<li>Изнутри Shadow DOM можно стилизовать только сам Shadow DOM и узлы, показываемые в `<content>`.</li>
|
||||
<li>Со страницы можно иметь доступ и стилизовать элементы, изначально находящиеся внутри хозяина -- напрямую, а узлы внутри Shadow DOM -- при помощи селекторов `::shadow` и `/deep/`.</li>
|
||||
</ul>
|
||||
[/summary]
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
|
@ -493,16 +164,12 @@ Shadow DOM -- это средство для создания отдельног
|
|||
|
||||
<ul>
|
||||
<li>Ряд браузерных элементов со сложной структурой уже имеют Shadow DOM.</li>
|
||||
<li>Можно создать Shadow DOM внутри любого элемента вызовом `elem.createShadowRoot()`. В дальнейшем его корень будет доступен как `elem.shadowRoot`.</li>
|
||||
<li>Можно создать Shadow DOM внутри любого элемента вызовом `elem.createShadowRoot()`. В дальнейшем его корень будет доступен как `elem.shadowRoot`. У встроенных элементов он недоступен.</li>
|
||||
<li>Как только у элемента появляется Shadow DOM, его изначальное содержимое скрывается. Теперь показывается только Shadow DOM, который может указать, какое содержимое хозяина куда вставлять, при помощи элемента `<content>`. Можно указать селектор `<content select="селектор">` и размещать разное содержимое в разных местах Shadow DOM.</li>
|
||||
<li>Стили и `querySelector`, объявленные внутри Shadow DOM, по умолчанию относятся только к его содержимому, могут обращаться к содержимому `<content>`, но не к основной странице.</li>
|
||||
<li>Стили и `querySelector` с внешней страницы могут преодолевать границу между DOM при помощи селекторов `::shadow` и `/deep/`.</li>
|
||||
<li>Элемент `<content>` перемещает содержимое исходного элемента в Shadow DOM только визуально, в структуре DOM оно остаётся на тех же местах.</li>
|
||||
</ul>
|
||||
|
||||
Спецификации, затрагивающие Shadow DOM:
|
||||
Подробнее спецификация описана по адресу [](http://w3c.github.io/webcomponents/spec/shadow/).
|
||||
|
||||
Далее мы рассмотрим работу с шаблонами, которые также являются частью платформы Web Components и не заменяют существующие шаблонные системы, но дополняют их важными встроенными в браузер возможностями.
|
||||
|
||||
<ul>
|
||||
<li>[Shadow DOM](http://w3c.github.io/webcomponents/spec/shadow/) -- самая полная спецификация по свойствам и методам Shadow DOM, деталям обработки событий.</li>
|
||||
<li>[Introduction to Web Components](http://w3c.github.io/webcomponents/explainer/) -- обо всём понемногу.</li>
|
||||
<li>[CSS Scoping](http://drafts.csswg.org/css-scoping/) -- спецификация по CSS-селекторам, в том числе Shadow DOM.</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<link rel="import" href="ui-tabs.html">
|
||||
<link rel="import" href="ui-dialog.html">
|
||||
...
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
|
||||
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
|
||||
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
|
||||
<script> alert('Библиотеки подключены!'); </script>
|
|
@ -1,2 +0,0 @@
|
|||
<link rel="import" href="libs.html">
|
||||
...template и код для диалогов...
|
|
@ -1,2 +0,0 @@
|
|||
<link rel="import" href="libs.html">
|
||||
...template и код для табов...
|
|
@ -1,7 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="import" id="link" href="timer.html">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
#timer { color: red; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p id="timer">0</p>
|
||||
|
||||
<script>
|
||||
var localDocument = document.currentScript.ownerDocument;
|
||||
var timer = localDocument.getElementById('timer');
|
||||
|
||||
var timerId = setInterval(function() {
|
||||
timer.innerHTML++;
|
||||
}, 1000);
|
||||
|
||||
document.body.appendChild(timer);
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
58
3-more/1-webcomponents/4-template-tag/article.md
Normal file
58
3-more/1-webcomponents/4-template-tag/article.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
|
||||
# Шаблоны <template>
|
||||
|
||||
Элемент `<template>` предназначен для хранения "образца" разметки, невидимого и предназначенного для вставки куда-либо.
|
||||
|
||||
Конечно, есть много способов записать произвольный невидимый текст в HTML. В чём же особенность `<template>`?
|
||||
|
||||
Его отличие от обычных тегов в том, что его содержимое обрабатывается особым образом. Оно не только скрыто, но и считается находящимся вообще "вне документа". А при вставке автоматически "оживает", выполняются из него скрипты, начинает проигрываться видео и т.п.
|
||||
|
||||
[cut]
|
||||
|
||||
Содержимое тега `<template>`, в отличие, к примеру, от шаблонов или `<script type="неизвестный тип">`, обрабатывается браузером. А значит, должно быть корректным HTML.
|
||||
|
||||
Оно доступно как `DocumentFragment` в свойстве тега `content`. Предполагается, что мы, при необходимости, возьмём `content` и вставим, куда надо.
|
||||
|
||||
## Вставка шаблона
|
||||
|
||||
Пример вставки шаблона `tmpl` в Shadow DOM элемента `elem`:
|
||||
|
||||
```html
|
||||
<!--+ run autorun="no-epub" -->
|
||||
|
||||
<p id="elem">Доброе утро, страна!</p>
|
||||
|
||||
<template id="tmpl">
|
||||
<h3><content></content></h3>
|
||||
<p>Привет из подполья!</p>
|
||||
<script> document.write('...document.write:Новость!'); </script>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
var root = elem.createShadowRoot();
|
||||
root.appendChild( tmpl.content.cloneNode(true) );
|
||||
</script>
|
||||
```
|
||||
|
||||
У нас получилось, что:
|
||||
<ol>
|
||||
<li>В элементе `#elem` содержатся данные в некоторой оговорённой разметке.</li>
|
||||
<li>Шаблон `#tmpl` указывает, как их отобразить, куда и в какие HTML-теги завернуть содержимое `#elem`.</li>
|
||||
<li>Здесь шаблон показывается в Shadow DOM тега. Технически, это не обязательно, шаблон можно использовать и без Shadow DOM, но тогда не сработает тег `<content>`.</li>
|
||||
</ol>
|
||||
|
||||
Можно также заметить, что в скрипт из шаблона выполнился. Это важнейшее отличие вставки шаблона от вставки HTML через `innerHTML` и от обычного `DocumentFragment`.
|
||||
|
||||
Также мы вставили не сам `tmpl.content`, а его клон. Это обычная практика, чтобы можно было использовать один шаблон много раз.
|
||||
|
||||
## Итого
|
||||
|
||||
Тег `<template>` не призван заменить системы шаблонизации. В нём нет хитрых операторов итерации, привязок к данным.
|
||||
|
||||
Его основная особенность -- это возможность вставки "живого" содержимого, вместе со скриптами.
|
||||
|
||||
И, конечно, мелочь, но удобно, что он не требует никаких библиотек.
|
||||
|
||||
|
||||
|
||||
|
291
3-more/1-webcomponents/5-css-scoping/article.md
Normal file
291
3-more/1-webcomponents/5-css-scoping/article.md
Normal 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>
|
||||
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<script src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
Это хорошо, когда нужно действительно в одной странице отобразить содержимое другой.
|
||||
|
||||
А что, если нужно встроить другой документ как естественную часть текущего? С единым скриптовым пространством, едиными стилями. И желательно не иметь проблем с разными доменами: если уж мы действительно хотим подключить HTML с одного домена в страницу на другом -- мы должны иметь возможность это сделать без "плясок с бубном".
|
||||
А что, если нужно встроить другой документ как естественную часть текущего? С единым скриптовым пространством, едиными стилями, но при этом -- другой документ.
|
||||
|
||||
Именно для этого предназначен `<link rel="import" href="...">`.
|
||||
Например, это нужно для подгрузки внешних частей документа (веб-компонент) снаружи. И желательно не иметь проблем с разными доменами: если уж мы действительно хотим подключить HTML с одного домена в страницу на другом -- мы должны иметь возможность это сделать без "плясок с бубном".
|
||||
|
||||
Иначе говоря, `<link rel="import">` -- это аналог `<script>`, но для подключения не только скриптов, а документов, с шаблонами, библиотеками, веб-компонентами и т.п. Всё станет понятнее, когда мы посмотрим детали.
|
||||
|
||||
## Пример вставки
|
||||
|
||||
|
@ -29,7 +31,7 @@
|
|||
```
|
||||
|
||||
<ul>
|
||||
<li>В отличие от `<iframe>` тег `<link rel="import">` может быть в любом месте документа.</li>
|
||||
<li>В отличие от `<iframe>` тег `<link rel="import">` может быть в любом месте документа, даже в `<head>`.</li>
|
||||
<li>При вставке через `<iframe>` документ показывается внутри фрейма. В случае с `<link rel="import">` это не так, по умолчанию документ вообще не показывается.</li>
|
||||
</ul>
|
||||
|
||||
|
@ -39,65 +41,43 @@
|
|||
|
||||
Мы сами решаем, где и когда его вставить.
|
||||
|
||||
Например:
|
||||
[iframe src="import-show" link edit height="60" border="1"]
|
||||
|
||||
Основной документ:
|
||||
В примере ниже `<link rel="import" href="timer.html">` подключает документ `timer.html` и, после его загрузки, вызывает функцию `show`. Эта функция через `link.import.querySelector('time')` выбирает интересующую часть подгруженного документа и вставляет её в текущий:
|
||||
|
||||
```html
|
||||
<!--+ src="index.html" -->
|
||||
<!--+ src="import-show/index.html" -->
|
||||
```
|
||||
|
||||
Важные детали:
|
||||
<ul>
|
||||
<li>Загрузка осуществляется асинхронно, для того чтобы поймать момент загрузки -- используется событие `onload`, для ошибки -- `onerror`.</li>
|
||||
<li>Подгруженный документ доступен как `link.import`. Это полноценный HTML-документ.</li>
|
||||
</ul>
|
||||
|
||||
Файл `timer.html`:
|
||||
В файле `timer.html` находится элемент и скрипт, который его "оживляет":
|
||||
|
||||
```html
|
||||
<!--+ src="timer.html" -->
|
||||
<!--+ src="import-show/timer.html" -->
|
||||
```
|
||||
|
||||
[codetabs src="import-show" height=350]
|
||||
|
||||
Важные детали:
|
||||
<ul>
|
||||
<li>После загрузки все скрипты в подключённом `timer.html` выполняются в контексте основной страницы, так что `timer` и другие переменные станут глобальными переменными страницы.</li>
|
||||
<li>Переменная `document` -- это документ основной страницы. Для доступа к импортированному, то есть текущему документу его можно получить как `document.currentScript.ownerDocument`.</li>
|
||||
<li>Таймер в загруженном документе начинает работать сразу, новый документ активен, хотя до переноса узлов в основной документ этого не видно.</li>
|
||||
<li>Переменная `document` -- это документ основной страницы. Для доступа к импортированному, то есть текущему документу изнутри `timer.html` его можно получить как `document.currentScript.ownerDocument`.</li>
|
||||
<li>Таймер в загруженном документе начинает работать сразу, новый документ оживает сразу после загрузки, хотя до переноса узлов в основной документ этого может быть и не видно.</li>
|
||||
</ul>
|
||||
|
||||
В примере выше содержимым импорта управлял основной документ, но `timer.html` мог бы и показать сам себя вызовом `document.body.appendChild(timer)` или вызвать функцию с внешнего документа, так как у них единая область видимости. Тогда не понадобился бы никакой `onload`.
|
||||
|
||||
Ещё пример вставки:
|
||||
Ещё пример вставки, на этот раз документ только подключает `<link>`, а таймер вставляет себя сам:
|
||||
|
||||
[iframe src="import-style" link edit height="60" border="1"]
|
||||
[codetabs src="import-style" height="200"]
|
||||
|
||||
Основной документ:
|
||||
|
||||
```html
|
||||
<!--+ src="index.html" -->
|
||||
```
|
||||
|
||||
Сейчас он просто загружает `timer.html` и всё. А вставит себя импорт сам.
|
||||
|
||||
Файл `timer.html`:
|
||||
|
||||
```html
|
||||
<!--+ src="timer.html" -->
|
||||
```
|
||||
|
||||
**Обратим внимание -- стили импорта попадают в контекст страницы.**
|
||||
|
||||
В примере выше импорт добавил и стиль для `#timer` и сам элемент.
|
||||
Обратим внимание -- стили импорта попадают в контекст страницы. В примере выше импорт добавил и стиль для `#timer` и сам элемент.
|
||||
|
||||
## Веб-компоненты
|
||||
|
||||
Импорт задуман как часть платформы веб-компонент.
|
||||
Импорт задуман как часть платформы Web Components.
|
||||
|
||||
Предполагается, что главный документ может импортировать файлы-определения, в которых будут все необходимые HTML, JS и CSS для элементов:
|
||||
Предполагается, что главный документ может импортировать файлы-определения, в которых будут все необходимые HTML, JS и CSS для элементов, а затем использовать их.
|
||||
|
||||
Файл `index.html`:
|
||||
Пример:
|
||||
|
||||
```html
|
||||
<link rel="import" href="ui-tabs.html">
|
||||
|
@ -115,15 +95,6 @@
|
|||
|
||||
Если файл `libs.html` импортирован два раза, то CSS и скрипты из него подключатся и выполнятся ровно один раз.
|
||||
|
||||
Файл `libs.html`:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
|
||||
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
|
||||
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
|
||||
<script> alert('Библиотеки подключены!'); </script>
|
||||
```
|
||||
|
||||
Это можно использовать, чтобы не подгружать одинаковые зависимости много раз. И сама страница и её импорты, и их подимпорты, и так далее, могут подключать `libs.html` без опасения лишний раз перезагрузить и выполнить скрипты.
|
||||
|
||||
Например:
|
||||
|
@ -137,7 +108,7 @@
|
|||
```
|
||||
|
||||
</li>
|
||||
<li>`ui-tabs.html`:
|
||||
<li>`ui-tabs.html` подключает `libs.html`:
|
||||
|
||||
```html
|
||||
<link rel="import" href="libs.html">
|
||||
|
@ -146,7 +117,7 @@
|
|||
|
||||
</li>
|
||||
|
||||
<li>`ui-dialog.html`:
|
||||
<li>`ui-dialog.html` также использует `libs.html`:
|
||||
|
||||
```html
|
||||
<link rel="import" href="libs.html">
|
||||
|
@ -156,9 +127,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
[edit src="import-libs"/]
|
||||
|
||||
Открыв пример, вы увидите, что скрипты `libs.html` сработают один раз.
|
||||
Файл `libs.html` при этом будет подключен только один раз. Это позволяет не бояться лишнего дублирования библиотек, используемых при описании множества компонент.
|
||||
|
||||
## Итого
|
||||
|
||||
|
@ -168,6 +138,6 @@
|
|||
<li>Скриптовое пространство и стили со страницей будут общие.</li>
|
||||
<li>Документ DOM -- отдельный, он доступен как `link.import` снаружи, а из внутреннего скрипта -- через `document.currentScript.ownerDocument`. Можно без проблем переносить элементы из главного документа в импорт и наоборот.</li>
|
||||
<li>Импорты могут содержать другие импорты.</li>
|
||||
<li>Если какой-то URL импортируется повторно -- подключается уже готовый документ, без повторного выполнения скриптов в нём. Это можно использовать для удобного управления зависимостями.</li>
|
||||
<li>Если какой-то URL импортируется повторно -- подключается уже готовый документ, без повторного выполнения скриптов в нём. Это позволяет избежать дублирования при использовании одной библиотеки во множестве мест.</li>
|
||||
</ul>
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
<script>
|
||||
|
||||
function show() {
|
||||
var p = link.import.querySelector('p')
|
||||
document.body.appendChild(p);
|
||||
var time = link.import.querySelector('time')
|
||||
document.body.appendChild(time);
|
||||
};
|
||||
|
||||
</script>
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<p id="timer">0</p>
|
||||
<time id="timer">0</time>
|
||||
|
||||
<script>
|
||||
var localDocument = document.currentScript.ownerDocument;
|
|
@ -1,6 +1,6 @@
|
|||
# Веб-компонент в сборе
|
||||
|
||||
В этой главе мы посмотрим на расширенный пример веб-компонента, включающий в себя описанные ранее технологии: Custom Elements, Shadow DOM, CSS Scoping и, конечно же, Imports.
|
||||
В этой главе мы посмотрим на итоговый пример веб-компонента, включающий в себя описанные ранее технологии: Custom Elements, Shadow DOM, CSS Scoping и, конечно же, Imports.
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
Этот код ничем не отличается от использования обычного элемента, поэтому перейдём дальше, к содержимому `ui-message.html`
|
||||
|
||||
### Шаблон
|
||||
## Шаблон для ui-message
|
||||
|
||||
Файл `ui-message.html` можно начать с шаблона:
|
||||
|
||||
|
@ -64,21 +64,21 @@
|
|||
</template>
|
||||
```
|
||||
|
||||
Этот шаблон рисует `<div class="content">` и заполняет его содержимым элемента.
|
||||
Этот шаблон рисует `<div class="content">` и заполняет его содержимым элемента-хозяина.
|
||||
|
||||
Важные детали:
|
||||
<ul>
|
||||
<li>Самое важное правило здесь `:host { display:block }`.
|
||||
|
||||
Оно обязательно! . Это правило задаёт, что элемент-хозяин, то есть `<ui-message>`, будет иметь `display:block`. По умолчанию у элементов стоит `display: inline`, а это значит, что ни ширину ни `margin` указать не получится
|
||||
|
||||
Обратим внимание -- `display` относится к базовым свойствам элемента, поэтому задаётся в Shadow DOM. Внешняя страница надстраивает свои свойства поверх "стандартных".</li>
|
||||
<li>Последующие правила `:host(.info) .content` и `:host(.warning) .content` стилизуют уже не хозяина, а элемент `.content` в зависимости от того, какой на хозяине класс.</li>
|
||||
Оно обязательно! . Это правило задаёт, что корень DOM-дерева будет иметь `display:block`. По умолчанию `:host` не создаёт CSS-блок, а это значит, что ни ширину ни отступы указать не получится.</li>
|
||||
<li>Последующие правила `:host(.info) .content` и `:host(.warning) .content` стилизуют содержимое в зависимости от того, какой на хозяине класс.</li>
|
||||
</ul>
|
||||
|
||||
### Скрипт
|
||||
## Скрипт для ui-message
|
||||
|
||||
<script>
|
||||
В файле `ui-message.html` мы создадим новый элемент `<ui-message>`:
|
||||
|
||||
```js
|
||||
// (1) получить шаблон
|
||||
var localDocument = document.currentScript.ownerDocument;
|
||||
var tmpl = localDocument.getElementById('tmpl');
|
||||
|
@ -95,8 +95,7 @@ MessageProto.createdCallback = function() {
|
|||
document.registerElement('ui-message', {
|
||||
prototype: MessageProto
|
||||
});
|
||||
</script>
|
||||
[/html]
|
||||
```
|
||||
|
||||
Все компоненты этого кода мы подробно разбирали ранее:
|
||||
|
||||
|
@ -106,13 +105,15 @@ document.registerElement('ui-message', {
|
|||
<li>С момента регистрации все уже существующие элементы `<ui-message>` будут превращены в описанные здесь. И будущие, конечно, тоже.</li>
|
||||
</ol>
|
||||
|
||||
В действии:
|
||||
Компонент в действии:
|
||||
|
||||
[iframe src="message" border="1" edit link/]
|
||||
[codetabs src="message" height=200]
|
||||
|
||||
## Компонент ui-slider
|
||||
## Компонент ui-slider с jQuery
|
||||
|
||||
Теперь создадим слайдер с использованием библиотеки [jQuery UI](http://jqueryui.com).
|
||||
Компонент может использовать и внешние библиотеки.
|
||||
|
||||
Для примера создадим слайдер с использованием библиотеки [jQuery UI](http://jqueryui.com).
|
||||
|
||||
Компонент `ui-slider` будет показывать слайдер с минимальным и максимальным значением из атрибутов `min/max` и генерировать событие `slide` при его перемещении.
|
||||
|
||||
|
@ -132,7 +133,9 @@ document.registerElement('ui-message', {
|
|||
<div id="value">0</div>
|
||||
```
|
||||
|
||||
Файл `ui-slider.html` мы разберём по частям.
|
||||
## Файл компонента ui-slider
|
||||
|
||||
Файл `ui-slider.html`, задающий компонент, мы разберём по частям.
|
||||
|
||||
### Заголовок
|
||||
|
||||
|
@ -153,22 +156,21 @@ document.registerElement('ui-message', {
|
|||
Содержимое `jquery.html`:
|
||||
|
||||
```html
|
||||
<script src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
|
||||
<!--+ src="ui-slider/jquery.html" -->
|
||||
```
|
||||
|
||||
### Шаблон
|
||||
|
||||
Шаблон будет помещён в Shadow DOM. В нём должны быть стили и элементы, необходимые слайдеру.
|
||||
|
||||
Конкретно для слайдера из разметки достаточно одного элемента `<div id="slider"></div>, который затем будет обработан jQuery UI.
|
||||
Конкретно для слайдера из разметки достаточно одного элемента `<div id="slider"></div>`, который затем будет обработан jQuery UI.
|
||||
|
||||
Кроме того, в шаблоне должны быть стили:
|
||||
|
||||
```html
|
||||
<template id="tmpl">
|
||||
<style>
|
||||
@import url(http://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css);
|
||||
@import url(http://code.jquery.com/ui/1.11.3/themes/ui-lightness/jquery-ui.css);
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
|
@ -182,7 +184,7 @@ document.registerElement('ui-message', {
|
|||
|
||||
Скрипт для нового элемента похож на тот, что делали раньше, но теперь он использует jQuery UI для создания слайдера внутри своего Shadow DOM.
|
||||
|
||||
Для его понимания желательно знать jQuery, но я намеренно свёл использование этой библиотеки к минимуму. Ниже будет описание по шагам.
|
||||
Для его понимания желательно знать jQuery, хотя в коде ниже я намеренно свёл использование этой библиотеки к минимуму.
|
||||
|
||||
```js
|
||||
var localDocument = document.currentScript.ownerDocument;
|
||||
|
@ -230,7 +232,7 @@ document.registerElement('ui-slider', {
|
|||
|
||||
Полный код с примером:
|
||||
|
||||
[iframe src="ui-slider" edit link border="1"/]
|
||||
[codetabs src="ui-slider" height=300]
|
||||
|
||||
Его можно далее улучшать, например добавить геттер и сеттер для значения `value`:
|
||||
|
||||
|
@ -247,9 +249,9 @@ Object.defineProperty(SliderProto, 'value', {
|
|||
|
||||
Если добавить этот код, то к значению `<ui-slider>` можно будет обращаться как `elem.value`, аналогично всяким встроенным `<input>`.
|
||||
|
||||
**Попробуйте пример выше. Он не совсем работает!**
|
||||
## Проблема с jQuery
|
||||
|
||||
Слайдер прокручивается первый раз, но второй раз он как-то странно "прыгает".
|
||||
Попробуйте пример выше. Он не совсем работает. Слайдер прокручивается первый раз, но второй раз он как-то странно "прыгает".
|
||||
|
||||
Чтобы понять, почему это происходит, я заглянул в исходники jQuery UI и, после отладки происходящего, натолкнулся на проблемный код.
|
||||
|
||||
|
@ -270,7 +272,7 @@ if ( !jQuery.contains( elem.ownerDocument, elem ) ) {
|
|||
|
||||
Получилось, что элемент не в документе и одновременно он имеет размеры. Такого разработчики jQuery не предусмотрели.
|
||||
|
||||
Можно, конечно, побежать исправлять jQuery, но давайте подумаем.
|
||||
Можно, конечно, побежать исправлять jQuery, но давайте подумаем, может быть так оно и должно быть?
|
||||
|
||||
С точки зрения здравого смысла, Shadow DOM является частью текущего документа. Это соответствует и духу [текущей спецификации](http://w3c.github.io/webcomponents/spec/shadow/), где shadow tree рассматривается в контексте document tree.
|
||||
|
||||
|
@ -278,18 +280,19 @@ if ( !jQuery.contains( elem.ownerDocument, elem ) ) {
|
|||
|
||||
Почему же `false`? Причина проста -- описанный в [другом стандарте](http://www.w3.org/TR/dom/#dom-node-contains) механизм работы `contains` по сути состоит в проходе вверх от `elem` по цепочке `parentNode`, пока либо встретим искомый элемент, тогда ответ `true`, а иначе `false`. В случае с Shadow DOM этот путь закончится на корне Shadow DOM-дерева, оно ведь не является потомком хозяина. Метод `contains` не знает ничего про саму возможность Shadow DOM, поэтому и выходит, что результат `false`.
|
||||
|
||||
Так что срочно слать патчи в jQuery здесь рановато, скорее необходимо ещё подумать над стандартами.
|
||||
Это один из тех небольших, но важных нюансов, которые показывают, почему стандарты всё ещё в разработке.
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>С использованием современных технологий можно делать компоненты. Но это, всё же, дело будущего. Все стандарты находятся в процессе доработки, готовятся новые.</li>
|
||||
<li>На текущий момент нельзя взять произвольную библиотеку, даже такую распространённую как jQuery, и работать с Shadow DOM с её использованием. Выше была продемонстрирована одна проблема, но возможны и другие.</li>
|
||||
<li>Можно использовать произвольную библиотеку, такую как jQuery, и работать с Shadow DOM с её использованием. Но возможны проблемки. Выше была продемонстрирована одна из них, могут быть и другие.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
Самый известный из полифиллов на тему веб-компонент -- это [Polymer](http://www.polymer-project.org). Он старается их эмулировать по возможности кросс-браузерно, но пока что это довольно-таки сложно, в частности, необходима дополнительная разметка.
|
||||
|
||||
Текущее состояние веб-стандартов -- "взгляд в будущее". Наверно, будет здорово, когда оно наступит :)
|
||||
Текущее состояние веб-стандартов -- "взгляд в будущее". Наверно, будет здорово, когда оно наступит.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<script src="http://code.jquery.com/jquery-2.1.3.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<template id="tmpl">
|
||||
<style>
|
||||
@import url(http://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css);
|
||||
@import url(http://code.jquery.com/ui/1.11.3/themes/ui-lightness/jquery-ui.css);
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
|
@ -32,8 +32,8 @@ SliderProto.createdCallback = function() {
|
|||
var self = this;
|
||||
|
||||
this.$slider.slider({
|
||||
min: this.getAttribute('min') || 0,
|
||||
max: this.getAttribute('max') || 100,
|
||||
min: +this.getAttribute('min') || 0,
|
||||
max: +this.getAttribute('max') || 100,
|
||||
value: this.getAttribute('value') || 0,
|
||||
slide: function() {
|
||||
var event = new CustomEvent("slide", {
|
Loading…
Add table
Add a link
Reference in a new issue