Андрей Глазков, Ито Хаято из Google, а также другие специалисты на Github работают над составлением спецификации Shadow DOM. Уже проделана огромная работа, однако еще много предстоит сделать. В рамках поддержки работы на этом направлении создан перевод существующей версии спецификации от 7 июля.

Эта спецификация описывает способ объединения нескольких DOM-деревьев в одну иерархию, и взаимодействие этих деревьев друг с другом в одном документе, что позволяет построить DOM более правильно.


1. Соответствие
Все диаграммы, примеры, примечания, равно как и разделы, явно помеченные как ненормативные не являются нормативными. Все остальное в этой спецификации является нормативным.
В оригинальной версии спецификации ключевые слова «должен», «не должен», «необходимо», «будет», «не будет», «следует», «не следует», «рекомендуется», «может» и «опционально» в нормативных частях этого документа должны быть интерпретированы как описано в [RFC 2119]. Для удобства чтения эти слова не употребляются в верхнем регистре в оригинале данной спецификации.

Для упрощения отсылок и с целью избежать циклических зависимостей между различными частями спецификации, этот документ составлен из трех последовательных частей:

1. Создание условий для спецификации.
2. Объяснение идеи модели и алгоритмов, лежащих в её основе.
3. Описание этой модели при помощи DOM-интерфейсов и HTML-элементов.

В некотором смысле, эти части можно рассматривать как математику, устанавливающую среду, в которой будет действовать модель, физику, описывающую то, как теоретически данная модель должна работать и механику, показывающую применение модели на практике.

Любая точка, в которой соответствующее клиентское приложение должно принимать решения о состоянии или реагировать на состояние концептуальной модели, описана в виде алгоритма.

Алгоритмы описываются в условиях единообразия обработки.
Единообразие обработки — это ограничение, накладываемое на имплементаторов алгоритма, которое заключается в том, что вывод для всех реализованных на клиенте и описанных в спецификации алгоритмов должен быть абсолютно идентичен при любом вводе.

2. Понятия
2.1 Введение

Эта часть является ненормативной.
В качестве ненормативного введения следует рассматривать Shadow DOM 101.

2.2 Теневые деревья (Shadow Trees)

Дерево документа (document tree) — это структура DOM-узлов, корневым элементом которой является документ.

Теневое дерево (shadow tree) имеет соответствующий флаг для режима инкапсуляции, который может быть открытым или закрытым.

Теневой корень (shadow root) является корневым элементом теневого дерева.

Узел А называется дочерним (deep child) или вложенным теневым корнем узла В, если А является дочерним узлом В или А является корневым узлом теневого дерева, содержащегося в В.

Узел А называется глубоким потомком (deep descendant) узла В если А является дочерним или вложенным теневым корнем узла В или А является дочерним или вложенным теневым корнем узла С, являющегося в свою очередь глубоким потомком узла В.

Содержащийся глубокий потомок (inclusive deep descendant) может быть узлом или одним из его глубоких потомков.

Узел А называется глубоким родителем (deep parent) узла В только в случае, если В – глубокий потомок А.

Узел А называется глубоким предком (deep ancestor) узла В только в том случае, если В – глубокий потомок А.

Содержащийся глубокий предок (inclusive deep ancestor) – узел или один из его глубоких предков.

Когда элемент является содержащимся глубоким потомком элемента документа, он находится в документе на глубине этого элемента.

2.3 Деревья деревьев (Trees of trees)

Деревом деревьев называется дерево, состоящее из деревьев узлов (node trees).
Определение термину «дерево деревьев» дается в этом месте с целью облегчить определение алгоритмов в последующих разделах. Такая методика обозначения должна упростить данную спецификацию.

Точно так же, как дерево узлов можно представить в виде набора взаимоотношений между этими узлами, дерево деревьев может быть представлено в виде набора взаимоотношений между деревьями узлов.

? Дерево А является родительским деревом (parent tree) дерева В в случае, если удовлетворено одно из этих условий:

1. А и В находятся рядом друг с другом в одном упорядоченном списке и дерево А является старшим по отношению к В.
2. B является самым старшим теневым деревом и ведущий элемент теневого дерева B является ведущим и для A.

? Другие отношения и условия, такие как корневое дерево, дочернее дерево, дерево-потомок, содержащее дерево-потомок, дерево-предок, содержащее дерево-предок, предшествующее дерево определяются подобным к определению деревьев образом.

Свойство узла ownerDocument в теневом дереве должно ссылаться на документ ведущего элемента, содержащего это теневое дерево.

Именованные свойства объекта Window [HTML] должны иметь доступ к узлам в дереве документа.

2.3.1 Пример дерева деревьев
Эта часть является ненормативной.


Рис. 1 Дерево деревьев.

На рисунке изображено 6 деревьев-узлов с именами A, B, C, D, E и F. Деревья узлов C, D и B имеют общий ведущий элемент, являющийся частью дерева узлов A. Теневые деревья Е и F имеют общий ведущий элемент, являющийся частью дерева узлов D.
На рисунке изображены также следующие взаимоотношения:

? Список дочерних деревьев дерева A это [B, C, D].
? Список дочерних деревьев дерева B это [].
? Список дочерних деревьев дерева C это [].
? Список дочерних деревьев дерева D это [E, F].
? Список дочерних деревьев дерева E это[].
? Список дочерних деревьев дерева F это[].
? Дерево документов А — корневое дерево (root tree) дерева деревьев (tree of trees).

Что касается отношений между узлами, то стоит отметить, что не существует отношений вида предок / потомок между двумя узлами, если они состоят в различных деревьев узлов. Корневой элемент теневого дерева не является дочерним узлом ведущего элемента теневого дерева. Не существует родительского узла корневого элемента теневого дерева. По этой причине, большинство существующих API, имеют собственную область видимости, и не влияют на другие деревья узлов, несмотря на то, что они образуют одно дерево деревьев. К примеру, document.getElementById(elementId) никогда не вернет элемент из теневого дерева, даже если имеет указанный elementId.

То же касается и работы CSS селекторов: комбинированный селектор, спускающийся вниз по DOM дереву не будет влиять на элементы теневого дерева, потому что корневой элемент этого дерева не является дочерним узлом его ведущего элемента. Если используется специальный CSS селектор теневого DOM, который упоминается позже, он не будет соответствовать элементу в другом дереве узлов.

По той причине что ShadowRoot наследует DocumentFragment, как будет указано далее, можно использовать ShadowRoot.getElementByID(elementId) для получения узла из теневого дерева.

2.4 Составленные деревья (Composed trees)

Составленное дерево (composed tree) — это дерево узлов, построенное из узлов из нескольких деревьев узлов в дереве деревьев. Точный алгоритм построения такого дерева будет приведен далее.


Рис. 2 Составленное дерево

Если элемент не является частью составленного дерева, чей корневой узел является документом, этот элемент не должен появиться в структуре форматирования [CSS21] или создать какой либо блок. Такое поведение не должно быть переопределено при помощи явного указания свойства ‘display’.

При определении правил наследования CSS, элемент должен наследовать от родительского узла в составленном дереве, если это применимо.

Рабочий черновик спецификации CSS Scoping [css-scoping-1] определяет селекторы, относящиеся к теневой модели документа. Для тех, кто будет искать эту спецификацию теневой модели документа с этими селекторами, обозначим эти селекторы здесь:
? :shadow псевдоэлемент
? /deep/ комбинатор, который был заменен комбинатором >>> (теневой комбинатор частичных потомков)
? ::content псевдоэлемент
? :host-context() функциональный псевдокласс

3. Распределения
3.1 Точки включения

Точкой включения называется место, в котором появляются узлы в другом дереве узлов вместо их исходного положения при построении составленного дерева.


Рис. 3 Распределение

Распределение — это механизм, определяющий, какие узлы появляются в какой точке включения.Подробный алгоритм распределения будет описан позже.

3.2 Точки включения содержимого

Точки включения содержимого — это точки включения, к которым распределяются дочерние узлы ведущего теневого элемента. Элемент содержимого удовлетворяющий следующие условия представляет собой точку включения содержимого:

? Корневой узел элемента содержимого является корневым узлом теневого дерева
? Среди предков элемента содержимого нет никаких других элементов содержимого.

3.3 Результаты распределения

Каждое дерево деревьев имеет результаты распределения, описывающие следствие этого процесса. Результат распределения должен соответствовать следующему:

1. Каждая точка включения должна иметь упорядоченный список, называемый распределенными узлами, состоящий из узлов, распределенных в точку включения.
2. Каждый узел, не являющийся точкой включения имеет упорядоченный список, называемый назначениями точек включения и состоящий из точек включения в которые распределяются узлы.
3. Точка включения A является окончательным назначением узла B если A — последний элемент в списке назначений точек включения B.

Когда узел A распределен в точку включения B, должно произойти следующее:
? A добавится к распределенным узлам B
? B добавится к окончательным точкам включения A

Есть случай, который заслуживает особого внимания — ситуация, когда точка включения является дочерним узлом другого ведущего теневого элемента. В таких ситуациях, узлы распределенные в эту точку включения, появляются таким образом как будто они — дочерние узлы ведущего теневого элемента в контексте распределения. Тогда, узлы, распределенные в теневое дерево, могут быть уже распределены от своего родительского дерева.
Не смотря на то, что узел распределен в более чем одну точку включения, он появляется только один раз в составленном дереве — в месте своего окончательного назначения.



Рис. 4 Перераспределение.

На рисунке узел потомок 1 распределен в точку включения 1. Затем потомок 1 перераспределяется в точку включения 3. Окончательные точки включения потомок 1 — [точка включения 1, точка включения 3] и точка включения 3 являются окончательными назначениями потомка 1. Распределенные узлы точки включения 1 и точки включения 3 это [потомок 1] и [потомок 1, потомок 3], соответственно.

3.5 Алгоритмы распределения

Для вычисления результата распределения дерева деревьев необходимо использовать алгоритм распределения, который должен быть эквивалентен последовательному выполнению следующих шагов:

Ввод
ДЕРЕВО ДЕРЕВЬЕВ — дерево деревьев (tree of trees)
Вывод
Обновленный результат распределения ДЕРЕВА ДЕРЕВЬЕВ
1. Все распределенные узлы и окончательные точки включения принадлежащие ДЕРЕВУ ДЕРЕВЬЕВ должны быть пустыми.
2. КОРНЕВОЕ ДЕРЕВО должно быть корневым деревом ДЕРЕВА ДЕРЕВЬЕВ.
3. Необходимо запустить алгоритм вычисления распределения с КОРНЕВЫМ ДЕРЕВОМ в качестве вводных данных.

Для определения результата распределения для данного дерева узлов и его деревьев-потомков должен быть использован алгоритм вычисления результата распределения, заключающийся в выполнении следующих шагов:

Ввод
ДЕРЕВО УЗЛОВ — дерево узлов(node tree)
Вывод
Обновленный результат распределения для содержащих деревьев-потомков ДЕРЕВА УЗЛОВ
1. Для каждого ведущего элемента теневого дерева, ВЕДУЩЕГО ТЕНЕВОГО ЭЛЕМЕНТА (SHADOW-HOST), являющегося ведущим элементом ДЕРЕВА УЗЛОВ, в текущей древовидной структуре:

ПУЛ должен быть результатом работы алгоритма заполнения пула с ВЕДУЩИМ ТЕНЕВЫМ ЭЛЕМЕНТОМ в качестве вводных данных
ТЕНЕВОЕ ДЕРЕВО — это теневое дерево, которое содержится в ВЕДУЩЕМ ТЕНЕВОМ ЭЛЕМЕНТЕ
Запустить алгоритм распределения пула с ТЕНЕВЫМ ДЕРЕВОМ и ПУЛОМ в качестве входных данных
Запустить алгоритм вычисления результата распределения рекурсивно, с ТЕНЕВЫМ ДЕРЕВОМ в качестве входных данных

Алгоритм заполнения пула должен быть использован и для получения узлов из дочерних узлов данного узла и заключается в выполнении следующих действий:

Ввод
УЗЕЛ (NODE), узел
Вывод
ПУЛ (POOL), упорядоченный список узлов
1. ПУЛ должен быть пустым упорядоченным списком.
2. Для каждого узла, ПОТОМКА УЗЛА:
1. Если ПОТОМОК (CHILD) является точкой включения:
1. Добавить все узлы в распределенные узлы ПОТОМКА ПУЛА
2. Иначе:
1. Добавить ПОТОМКА в ПУЛ

Алгоритм распределения пула должен быть использован для того чтобы распределить узлы в пуле в точки включения содержимого в теневом дереве и заключается в выполнении следующих действий:

Ввод
ТЕНЕВОЕ ДЕРЕВО, теневое дерево
ПУЛ, упорядоченный список узлов
Вывод
Узлы в ПУЛЕ распределяются в точки включения содержимого в дереве.
1. Для каждой точки включения содержимого, СОДЕРЖИМОЕ, состоящее в ТЕНЕВОМ ДЕРЕВЕ в древовидной структуре:
1. Для каждого узла УЗЛА в ПУЛЕ
1. Если УЗЕЛ удовлетворяет критерию соответствия СОДЕРЖИМОГО:
1. Распределить УЗЕЛ в СОДЕРЖИМОЕ
2. Удалить УЗЕЛ из ПУЛА
2. Если в СОДЕРЖИМОЕ не распределен ни один узел:
1. Для каждого потомка, ПОТОМКА СОДЕРЖИМОГО
1. Распределить ПОТОМКА в СОДЕРЖИМОЕ

Если ни один узел не был распределен в точку включения содержимого СОДЕРЖИМОЕ, дочерние узлы СОДЕРЖИМОГО распределяются в СОДЕРЖИМОЕ в качестве резервных узлов.

Если есть какое-либо условие, которое влияет на изменения результатов распределения, результат распределения должен быть обновлен перед тем как его можно будет использовать.

3.5 Удовлетворение критериев соответствия

Критерием соответствия точке включения является набор составных селекторов [SELECTORS4]. Эти составные селекторы могут содержать только такие простые селекторы:

? Селектор по типу или универсальный селектор
? селектор(ы) класса
? Селектор ID
? селектор(ы) аттрибута
? Псевдокласс отрицания, :not()

Узел удовлетворяет критерию соответствия только в том случае если:
1. все сложные селекторы в выборке состоят только из разрешенных простых селекторов; и
2. узел соответствует по меньшей мере одному составному селектору в выборке или выборка является пустой.

4. Составление
Для определения узлов-потомков каждого узла в составленном дереве необходимо применять алгоритм вычисления потомков составленного дерева, заключающийся в выполнении следующих действий:

Ввод
УЗЕЛ (NODE), узел, находящийся в составленном дереве
Вывод
ПОТОМОК (CHILDREN), дочерний узел УЗЛА в составленном дереве.
1. ПОТОМОК должен быть пустым упорядоченным списком узлов
2. Если УЗЕЛ является ведущим теневым элементом:
1. ПУЛ-ПОТОМОК должен быть самым младшим теневым корнем, содержащимся в УЗЛЕ.
3. Иначе:
1. ПУЛ-ПОТОМОК должен быть дочерним узлом УЗЛА
4. Для каждого дочернего узла, ПОТОМКА, в ПУЛЕ-ПОТОМКЕ:
1. Если ПОТОМОК является точкой включения:
1. Для каждого узла, РАСПРЕДЕЛЕННОГО УЗЛА (DISTRIBUTED-NODE), в списке распределенных узлов в точке включения ПОТОМОК:
1. Если ПОТОМОК является окончательным назначением РАСПРЕДЕЛЕННОГО УЗЛА, добавить РАСПРЕДЕЛЕННЫЙ УЗЕЛ к ПОТОМКУ
2. Иначе:
1. Добавить ПОТОМОК к ПОТОМКАМ

Для данного дерева деревьев ДЕРЕВА ДЕРЕВЬЕВ, составленное дерево, построенное из ДЕРЕВА ДЕРЕВЬЕВ должно соответствовать следующему дереву:
? Корневой узел составленного дерева является корневым узлом корневого дерева в ДЕРЕВЕ ДЕРЕВЬЕВ.
Для данного узла, находящегося в составленном дереве, дочерние узлы этого узла являются результатом работы алгоритма расчета составленного дерева с узлом в качестве входных данных

5. События
В каждом алгоритме в этом разделе, Window необходимо рассматривать как если бы это был родительский узел документа — чтобы Window также мог получать события.

Когда событие распределяется в теневое дерево, его путь или проходит по теневым деревьям или заканчивается в теневом корне. Единственное исключение — события изменения(мутации). Такие типы событий никогда не должны распределяться в теневое дерево.

5.1 Всегда останавливаемые события
Следующие события всегда должны быть остановлены в самом младшем теневом корне:

? abort
? error
? select
? change
? load
? reset
? resize
? scroll
? selectstart

Точный алгоритм будет приведен ниже.

5.2 Пути событий
Для определения пути события необходимо использовать алгоритм расчёта пути событий, заключающийся в выполнении следующих действий:
Ввод
УЗЕЛ, узел (node)
СОБЫТИЕ, событие (event)
Вывод
ПУТЬ, путь события, упорядоченный список целей события.
1. ПУТЬ должен быть пустым упорядоченным списком.
2. ТЕКУЩИЙ ЭЛЕМЕНТ должен быть УЗЛОМ
3. Повторять до тех пор пока ТЕКУЩИЙ ЭЛЕМЕНТ существует:
1. Добавить ТОЧКУ ВКЛЮЧЕНИЯ в ПУТЬ
2. Если окончательные точки включения ТЕКУЩЕГО ЭЛЕМЕНТА не являются пустыми:
1. Добавить каждую точку включения в окончательные точки включения ТЕКУЩЕГО ЭЛЕМЕНТА, от первого, включающего, до последнего, исключающего, в ПУТЬ.
2… ТЕКУЩИЙ ЭЛЕМЕНТ должен быть окончательным назначением ТЕКУЩЕГО ЭЛЕМЕНТА
3. Иначе, если все следующие условия соблюдены, остановить этот алгоритм:
1. Если ТЕКУЩИЙ ЭЛЕМЕНТ является теневым корнем:
2. Если ТЕКУЩИЙ ЭЛЕМЕНТ — это корневой узел УЗЛА
3. Установлен scoped flag.
4. Иначе: ТЕКУЩИЙ ЭЛЕМЕНТ должен быть глубоким родителем (deep parent) ТЕКУЩЕГО ЭЛЕМЕНТА.

5.3 Примеры путей событий

Этот раздел является ненормативным
Предположим у нас есть следующее дерево деревьев:

Рис. 5 Пример дерева деревьев. Узлы, которые не участвуют в рассматриваемом пути события, описанном ниже, опущены.
? A это документ.
? E, J, N, Q, S — это теневые корни.
? I, M, P, R и U точки включения содержимого.

Это дерево деревьев состоит из следующих шести деревьев: одного дерева документа и пяти теневых деревьев:
? Дерево документа 1. В него входят узлы A, B, C и D.
? Теневое дерево 2 содержащееся в B. В него входят узлы E, F, G, H и I.
? Теневое дерево 3 содержащееся в H. В него входят узлы J, K, L и M.
? Теневое дерево 4 содержащееся в K. В него входят узлы N, O и P.
? Теневое дерево 5 содержащееся в O. В него входят узлы Q и R.
? Теневое дерево 6 содержащееся в F. В него входят узлы S, T и U.

Давайте предположим, что результат распределения этого дерева деревьев таков:

? Назначения точек включения C это [I, M] (C перераспределена)
? Назначения точек включения L это [P, R] (L перераспределена)
? Назначения точек включения G это [U]

В этом случае, если событие отправляется на узел D, путь события будет следующим:
[D, C, I, M, L, P, R, Q, O, N, K, J, H, G, U, T, S, F, E, B, A] (Без учета Window)

Обратите внимание, что алгоритм расчета пути события разработан для достижения следующих целей:

1. Если в пути события есть узел ПОТОМОК и у него есть РОДИТЕЛЬ в дереве узлов, путь события также будет включать и РОДИТЕЛЯ. РОДИТЕЛЬ всегда появляется где-нибудь после ПОТОМКА в пути события.
2. Узлы в пути события образуют линейную цепочку предков в каждом дереве узлов. Ни в одном дереве узлов нет точек ветвления.


Рис. 6 На этом рисунке цифра в левой части каждого узла отображает начальное положение каждого узла в пути событий. У родительского узла эта цифра всегда больше, чем у любого из его потомков в каждом дереве узлов.

Это говорит о том, что если мы сосредоточимся на одном дереве узлов и забудем обо всех остальных, путь события будет выглядеть так, как если бы событие произошло только в том дереве узлов, о котором идёт речь. Это — важный аспект в том смысле, что наличие теневого дерева никаким образом не влияет на путь события внутри того дерева узлов, в котором содержится ведущий теневой элемент, до тех пор пока событие не будет остановлено в каком-либо из деревьев-потомков.
Например, с точки зрения дерева документа 1, путь события должен выглядеть так: [D, C, B, A]. С точки зрения теневого дерева 2, путь события должен выглядеть как [I, H, G, F, E]. Похожим правилам подчиняются и другие деревья узлов.
Стоит также отметить, что если исключить все точки включения и теневые корни из пути событий, результат был бы эквивалентен содержащим предкам узла в составленном дереве на котором происходит событие.



Рис. 7 Соотношение между путем событий и составленным деревом. Путь события, используемого в примере показан на рисунке слева, а составленное дерево — с правой. Если исключить все точки включения и теневые корни из пути события, результат будет соответствовать содержащим предкам узла D в составленном дереве.

5.4 Перенаправление событий
В тех случаях, когда путь события проходит по нескольким деревьям узлов, информация события о его цели может регулироваться в зависимости от порядка обработки инкапсуляции. Перенаправление событий это процесс вычисления соответствующих целевых элементов для каждого предка узла, в котором происходит событие. Относительный целевой элемент это узел, наиболее точно представляющий целевой элемент события данного предка с сохранением инкапсуляции.
Алгоритм перенаправления должен быть использован для определения относительных целей и заключаться в выполнении следующих шагов:
Ввод
ТЕКУЩИЙ ПУТЬ, путь события
ТЕКУЩАЯ ЦЕЛЬ, узел на котором срабатывает слушатель события.
Вывод
ОТНОСИТЕЛЬНАЯ ЦЕЛЬ, скорректированный целевой элемент
1. ДЕРЕВО ТЕКУЩЕЙ ЦЕЛИ должно быть деревом узлов, в котором содержится ТЕКУЩАЯ ЦЕЛЬ
2. ПЕРВОНАЧАЛЬНАЯ ЦЕЛЬ должна быть первым элементом в ПУТИ СОБЫТИЯ
3. ДЕРЕВО ПЕРВОНАЧАЛЬНОЙ ЦЕЛИ должно быть деревом узлов, в котором содержится ПЕРВОНАЧАЛЬНАЯ ЦЕЛЬ
4. ДЕРЕВО ОТНОСИТЕЛЬНОЙ ЦЕЛИ должно быть ближайшим общим содержащим деревом потомков ДЕРЕВА ТЕКУЩЕЙ ЦЕЛИ и ДЕРЕВА ОТНОСИТЕЛЬНОЙ ЦЕЛИ.
5. ОТНОСИТЕЛЬНЫЙ ЦЕЛЕВОЙ ЭЛЕМЕНТ должен быть первым узлом в ПУТИ СОБЫТИЯ, удовлетворяющим следующим условиям:
1. Узел должен содержаться в ДЕРЕВЕ ОТНОСИТЕЛЬНОГО ЦЕЛЕВОГО ЭЛЕМЕНТА
Процесс перенаправления должен происходить до того, как событие будет доставлено элементу.

5.5 Перенаправление relatedTarget
У некоторых событий есть свойство relatedTarget [DOM-Level-3-Events], которое содержит узел не являющийся целью события, но имеющий к нему отношение.
Например, для события mouseover, relatedTarget может указывать на узел, с которого указатель мыши перешел на текущий элемент. В случае, когда relatedTarget находится в теневом дереве, то соответствующие пользовательские агенты не должны допускать утечек его фактического значения за пределы этого дерева. В тех случаях, когда и relatedTarget и target принадлежат к одному и тому же теневому дереву, то соответствующие пользовательские агенты должны остановить события в теневом корне, чтобы избежать появления побочных mouseover и mouseout событий одновременно из одного узла.

Таким образом, если событие имеет свойство relatedTarget, его значение и элемент который должен ему соответствовать должны быть скорректированы.
Процесс перенаправления relatedTarget должен происходить прежде, чем событие будет доставлено элементу.

5.6 Перенаправление событий прикосновения
Атрибут Touch target [СОБЫТИЯ ПРИКОСНОВЕНИЯ] должен быть скорректирован таким же образом, как событие с relatedTarget. Каждый аттрибут Touch target в списке TouchList, возвращаемый вызовом TouchEvent touches(), changedTouches() и targetTouches() должен быть результатом выполнения алгоритма расчета относительной цели с УЗЛОМ и Touch target в качестве вводных данных.

5.7 Перенаправление событий фокуса
События focus, DOMFocusIn, blur, и DOMFocusOut должны быть обработаны таким же образом, как события с relatedTarget, где потеря узлом фокуса является результатом получения этого фокуса другим узлом или целевым элементом события, что приводит к тому, что узел потерявший фокус становится относительной целью по отношению к узлу его получившему.

5.8 Передача событий
В момент передачи события:

? Атрибуты Event target и currentTarget должны возвращать относительную цель для узла на котором сработали слушатели события
? Атрибут relatedTarget события MouseEvent должен возвращать скорректированную относительную цель
? Атрибуты offsetX и offsetY события MouseEvent должны возвращать значение точки начала координат относительной цели с учётом внутренних отступов.
? Атрибут Touch target должен возвращать скорректированную относительную цель
? Если relatedTarget и target это один и тот же элемент для данного узла, слушатели событий не должны на нем срабатывать. TouchEvent этому правилу не подчиняется.
? Если узел является собственной относительной целью, слушатели событий не должны на нем срабатывать во время фазы захвата, влекущей за собой шаг 6 алгоритма передачи событий.
? Если узел, на котором сработал слушатель события является собственной относительной целью, атрибут eventPhase события должен возвращать значение AT_TARGET во время фазы всплытия, влекущей за собой шаг 9 алгоритма передачи событий.
? Если значение атрибута bubbles события равно false, должно быть выполнено следующее:
1. Путь события должен быть сортирован в обратном порядке
2. Значение атрибута события eventPhase должно быть установлено в AT_TARGET
3. Для каждого объекта в пути события относительная цель которого равна самому объекту, должен быть вызван слушатель события с самим событием в качестве аргумента до тех пор, пока флаг события останавливающий всплытие не будет отключен
После завершения передачи события объекты target и currentTarget события должны указывать на относительную цель наивысшего родителя.
Этот шаг является необходимым для того, чтобы можно было избежать обнаружения узлов в теневом дереве, так как для скрипта представляется возможным передать объект события за пределы его области видимости.

5.9 Пример перенаправления события
Этот раздел является ненормативным.
Предположим, у нас есть интерфейс пользователя, медиаконтроллер, составленный из дерева документа и теневых деревьев. В этом примере мы также предположим, что селекторы способны пересекать границы теневого дерева и попытаемся использовать эти селекторы для того, чтобы идентифицировать элементы. Также мы изобретем вымышленный элемент теневого корня чтобы разделить теневые границы и представить теневые корни:
ПРИМЕР 1



Допустим, пользователь располагает своё указывающее устройство над линейным переключателем громкости (#volume-slider-thumb), это вызывает событие mouseover на этом узле. Давайте также представим что для этого события не существует relatedTarget.
Согласно алгоритму перенаправления, получим следующие значения родительских элементов и относительных целей:
Родитель (Ancestor) Относительная Цель (Relative Target)


После доставки события mouseover с этими новыми относительными целями, пользователь решает поместить указатель над фейдером регулятора громкости (#timeline-slider-thumb). Это вызовет событие mouseout на переключателе шкалы громкости и mouseover на фейдере.
Давайте посмотрим, как это повлияет на значение relatedTarget события mouseout фейдера. Для этого события, relatedTarget это и есть фейдер (#timeline-slider-thumb). Согласно алгоритму расчета относительной цели, мы должны получить следующие значения для родительских элементов, относительных и скорректированных относительных целей:
Родитель (Ancestor) Относительная Цель (Relative Target) Скорректированная относительная Цель (Adjusted related Target)

Узел, #player, имеет свойства target и relatedTarget указывающие на одинаковое значение (#player), что говорит о том, что мы не передали событие этому узлу и его предкам.

6. Пользовательское взаимодействие
6.1 Диапазоны и Выборки
Этот раздел является ненормативным.
Выборка [РЕДАКТИРОВАНИЕ] не определена. Имплементация должна быть выполнена как можно лучше. Вот одно из возможных, но, в общем, наивных решений:
Так как узлы, находящиеся в разных деревьях, никогда не будут иметь общего корневого элемента, может никогда не существовать допустимого диапазона DOM, охватывающего несколько деревьев узлов.
Соответственно, выборки могут существовать только в пределах одного дерева узлов, так как они определены в одном диапазоне. Выборка, возвращаемая вызовом метода window.getSelection() никогда не может быть таковой, которая содержится в теневом дереве.
Метод объекта теневого корня getSelection() возвращает текущую выборку для текущего теневого дерева.

6.2 Фокусная навигация
Если узел не присутствует в составленном дереве, он должен быть пропущен в списке навигации [CSS3UI].
В случае последовательной фокусной навигации, очередь навигации для данного теневого дерева А должна быть включена в очередь навигации родительского дерева В следующим образом:
1. ХОСТ должен быть ведущим теневым элементом, содержащим А
2. Очередь навигации для А должна быть включена в очередь навигации для В:
1. сразу после ХОСТА, если ХОСТ — focusable; или
2. вместо ХОСТА как если бы ХОСТ получил значение для определения своей позиции автоматически.
В случае направленной фокусной навигации, вопрос интеграции очереди навигации теневых деревьев в очередь навигации документа остается за пользовательским агентом.

6.3 Активный Элемент
Для поддержания инкапсуляции, значение свойства activeElement объекта focus API документа также должно быть скорректировано. Для предотвращения потери информации при настройке этого значения, каждый теневой корень также должен иметь свойство activeElement для хранения значения сфокусированного элемента в теневом дереве.
Алгоритм коррекции активного элемента должен быть использован для определения значения свойства activeElement, и заключается в выполнении следующих шагов:
Ввод
ЭЛЕМЕНТ (ELEMENT), сфокусированный элемент
КОРЕНЬ, документ или теневой корень
Вывод
СКОРРЕКТИРОВАННОЕ (ADJUSTED), скорректированное свойство activeElement КОРНЯ.
1. ПУТЬ должен быть результатом работы алгоритма расчета пути события с ЭЛЕМЕНТОМ и null в качестве вводных данных
2. СКОРРЕКТИРОВАННОЕ должно быть результатом работы алгоритма перенаправления с ПУТЁМ и КОРНЕМ в качестве вводных даннных

6.4 Редактирование
Значение атрибута contenteditable не должно распространяться из ведущего теневого элемента в его теневые деревья.

6.5 Вспомогательные Технологии
Пользовательские агенты при помощи вспомогательных технологий могут проникать в составленное дерево, что в свою очередь делает возможным полноценное использование семантики WAI-ARIA [WAI-ARIA] в теневых деревьях.

7. HTML-элементы в Теневых Деревьях (Shadow Trees)
7.1 Инертность HTML Элементов в теневых деревьях
Для сравнения, теневое дерево может быть рассмотрено как что-то среднее между просто частью документа и независимой сущностью фрагмента документа. С момента завершения рендеринга, теневое дерево стремится сохранить черты обычного дерева в документе. В то же время это инкапсуляция абстракции с целью не допустить влияния на дерево документа. Таким образом, HTML элементы должны вести себя, как указано [HTML] в теневых деревьях, с некоторыми исключениями.

Согласно [HTML], некоторые HTML элементы будет вести себя иначе, если они находятся в теневом дереве, а не в дереве документа, потому что их определения говорят о том что этот элемент должен быть в документе в качестве необходимого условия для его работы. Другими словами, они не должны работать, если они находятся в теневом дереве, даже когда они находятся глубоко в документе. Необходимо заполнить этот пробел, потому что ожидается, что большинство HTML элементов будет вести себя так же, как и в документе, при условии, что они находятся глубоко в документе. Для подробностей смотрите баги W3C 26365 и 27406. Ниже приводится предварительный обзор дискуссий в этих багах. Однако на данный момент охвачены ещё не все HTML элементы и их поведения. Если HTML элемент не приведен ниже, он должен быть рассмотрен в качестве активного элемента в теневом дереве. Мы пытаемся обновить сам [HTML] вместо того, чтобы выпускать мелкие патчи для каждого случая.

HTML Элементы классифицируются в следующие категории:
? Активные в теневом дереве
? Подмножество HTML элементов, которые должны вести себя так, как будто они находятся в дереве документа, даже когда они на самом деле в теневом дереве, до тех пор, пока они находятся глубоко в документе.
? Следующие HTML элементы должны относиться к этой категории:
0 dialog
0 iframe
0 style
? Инертные в теневом дереве:
? Подмножество HTML элементов, которые должны вести себя инертно, или как будто они не являются частью дерева документа, если они находятся в теневом дереве. Это согласуется с тем как HTML элементы будут вести себя в фрагменте документа
? Следующие HTML элементы должны относиться к этой категории:
0 base
0 link
? Инертные до тех пор, пока не отрендерятся:
? Подмножество HTML элементов, которые должны вести себя инертно, или как будто они не являются частью дерева документа до тех пор, пока они не будут отрендерены. Другими словами, если они не находятся в составленном дереве, чей корневой элемент — документ, они должны вести себя инертно.
? Следующие HTML элементы должны относиться к этой категории:
0 applet
0 embed
0 object

Предположим, например, что элемент object это дочерний узел ведущего элемента теневого дерева, но список его назначений точек включения — пуст. Тогда, в соответствии с алгоритмом распределения и алгоритмом расчета потомков составленного дерева, этот элемент никогда не будет находиться в составленном дереве, чей корневой узел — документ. Таким образом, этот элемент инертен, так как он не визуализируется.
7.2 Атрибуты
Когда [HTML] определяет алгоритмы обработки, для прохождения деревьев для следующих атрибутов, алгоритмы должны использовать составленное дерево.
? dir
? draggable
? dropzone
? hidden
? lang и xml:lang
? spellcheck
? title

Этот список не включает атрибуты, определенные в каком-нибудь другом месте в спецификации. Такие атрибуты как:
? tabindex и autofocus определенные в фокусной навигации.
? role и ARIA определенные во вспомогательных технологиях.


8. Элементы и DOM-интерфейсы. Примеры Shadow Dom. Ссылки
8.1 Интерфейс ShadowRoot
Интерфейс ShadowRoot представляет теневой корень.


8.1.1 Атрибуты
activeElement тип: Element, readonly, nullable
Представляет текущий сфокусированный элемент в теневом дереве.
Во время получения атрибут должен возвращать текущий элемент, находящийся в фокусе или ничего, если такового элемента нет.

host тип: Element, readonly
Представляет ведущий элемент теневого дерева, содержащий объект контекста
Во время получения атрибут должен возвращать ведущий элемент теневого дерева, содержащий объект контекста.

innerHTML тип: DOMString,
Представляет разметку содержимого ShadowRoot.
Во время получения атрибут должен возвращать результат работы алгоритма фрагментной сериализации HTML с объектом контекста в качестве shadow host.

Во время установки значений выполняются следующие шаги:
1. ФРАГМЕНТ должен быть результатом вызова алгоритма парсинга фрагмента [DOM-PARSING] с новым значением в качестве РАЗМЕТКИ и объектом контекста в качестве shadow host
2. Заменить всё на ФРАГМЕНТ в теневом корне.

olderShadowRoot тип: ShadowRoot, readonly, nullable
Представляет старший теневой корень по отношению к объекту контекста
При получении атрибут должен вернуть очередь StyleSheetList, содержащую таблицу стилей теневого корня.

8.1.2 Методы
caretPositionFromPoint

Это может быть определено примерно так же, как caretPositionFromPoint [CSSOM-VIEW]. Со временем это должно стать частью спецификации CSSOM View Module [CSSOM-VIEW]. Смотрите также баг W3C 27829.


Тип возвращаемого значения: CaretPosition, nullable

elementFromPoint
Возвращает элемент находящийся на указанных координатах.

Со временем это должно стать частью спецификации CSSOM View Module [CSSOM-VIEW]. Смотрите также баг W3C 27829.

При вызове должен вернуть результат выполнения следующий действий:
1. Если объект контекста — не экземпляр ShadowRoot — бросить ошибку InvalidNodeTypeError.
2. Если какой-либо аргумент отрицательный, x больше ширины вьюпорта без учета ширины скроллбара(если таковой имеется), или если y больше высоты вьюпорта без учета высоты скроллбара(если таковой имеется), вернуть null.
3. ПОПАДАНИЕ должно быть элементом с координатами x и y в видимой области доумента, определенным через тестирование попадания
4. ПУТЬ должен быть результатом выполнения алгоритма расчета пути события с ПОПАДАНИЕМ и null в качестве входных данных
5. Вернуть результат выполнения алгоритма перенаправления с ПУТЕМ и объектом контекста в качестве входных данных



Тип возвращаемого значения: Element, nullable
elementsFromPoint
Это может быть определено примерно так же, как elementFromPoint. Со временем это должно стать частью спецификации CSSOM View Module [CSSOM-VIEW]. Смотрите также баг W3C 27829.


Тип возвращаемого значения: очередьgetSelection
Возвращает текущую выборку в теневом дереве.
При вызове должен возвращать выборку в теневом дереве.
Нет параметров.
Тип возвращаемого значения: Selection, nullable
Атрибут nodeType экземпляра ShadowRoot должен возвращать DOCUMENT_FRAGMENT_NODE. Соответственно атрибут nodeName экземпляра ShadowRoot должен возвращать "#document-fragment".
Вызов метода cloneNode() экземпляра ShadowRoot должен всегда возбуждать исключение DATA_CLONE_ERR.

8.2 Расширение интерфейса Element


8.2.1 Атрибуты
shadowRoot тип: ShadowRoot, readonly, nullable
Представляет самый младший теневой корень, содержащийся в объекте контекста.
Во время получения атрибут должен возвращать самый младший теневой корень, содержащийся в объекте контекста, если таковой есть и является открытым. Иначе должен возвращать null.

8.2.2 Методы
createShadowRoot
При вызове должны быть выполнены следующие шаги:
1. Создать новый экземпляр объекта ShadowRoot, с открытым типом инкапсуляции.
2. Добавить объект ShadowRoot в упорядоченный список теневых корней в качестве самого младшего теневого корня объекта контекста
3. Вернуть объект ShadowRoot.
Нет параметров.
Тип возвращаемого значения: ShadowRoot
getDestinationInsertionPoints
При вызове метод должен вернуть статический NodeList, состоящий из точек включения в списке назначений точек включения объекта контекста, за исключением точек включения находящихся в закрытом теневом дереве.
Нет параметров.
Тип возвращаемого значения: NodeList

8.3 Расширение интерфейса Text
partial interface Text {
NodeList getDestinationInsertionPoints ();
};
8.3.1 Методы
getDestinationInsertionPoints
При вызове метод должен вернуть статический NodeList, состоящий из точек включения в списке назначений точек включения объекта контекста, за исключением точек включения находящихся в закрытом теневом дереве.
Нет параметров.
Тип возвращаемого значения: NodeList

8.4 Элемент content
Элемент content точки включения в теневом дереве.
Если элемент content не удовлетворяет состоянию точки включения, его поведение рендеринга должно быть таким же как и у HTMLUnknownElement.
Контекст
Там где ожидается потоковое содержимое.
Модель содержимого
Прозрачная
Потомки
Что угодно, в качестве резервного содержимого
Атрибуты содержимого
Глобальные атрибуты
select, выборка маркеров, разделенных запятой

Представляет собой критерий соответствия для распределенных дочерних узлов ведущего теневого элемента. Каждый маркер должен быть составным селектором.

DOM Интерфейс



Атрибуты
select тип: DOMString,
Должен отображать атрибут select.
Методы
getDistributedNodes
При вызове должен вернуть результат выполнения следующих шагов:
1. Если объект контекста — точка включения содержимого:
1. Вернуть статический NodeList состоящих из узлов, распределенных в объект контекста, за исключением узлов находящихся в закрытом теневом дереве.
2. Иначе:
1. Вернуть пустой статический NodeList.
Нет параметров.
Тип возвращаемого значения: NodeList

8.5 Элемент shadow
Элемент shadow представляет собой теневую точку включения в теневом дереве.
Если элемент shadow не удовлетворяет состоянию точки включения, его поведение рендеринга должно быть таким же как и у HTMLUnknownElement.
Контекст
Там где ожидается потоковое содержимое.
Модель содержимого
Прозрачная
Потомки
Любые

Примеры Shadow DOM

Боба попросили добавить простой перечень ссылок в виджет новостей, ссылки в котором поделены на две категории: свежие новости и просто новости. Сейчас разметка документа выглядит так:



Для организации заголовков Боб решил использовать теневой DOM. Это даёт Бобу возможность поддержать порядок в документе, а использование возможностей точек включения сильно упрощает задание сортировки заголовков по имени класса. После очередной чашечки Green Eye, он быстро набросал такое теневое дерево, содержащееся в элементе ul:



Потом Боб задал стили для новенького виджета в соответствии с указаниями дизайнера, добавив это в макет теневого дерева:



Подумывая о том, не нужен ли его компании новый дизайнер, Боб преобразует макет в код:



Отлично, Боб! Осталось ещё пол-чашки кофе, а работа уже сделана. Осознавая свою гениальность, Боб возвращается к обучения нубов игре в Puyo Puyo.
Некоторое время спустя.
Время выборов. Пока Боб на ежегодной конференции, Алиса получила задание добавить другой, временный, блок в новостной виджет, куда будет попадать всё, что связано с выборами. Алиса изучила код Боба, взглянула на спецификацию теневой модели документа и поняла, что, благодаря поддержке нескольких теневых деревьев, нет необходимости менять код, написанный Бобом. Её решение было как всегда элегантным и простым и отлично вписывалось в код, написанный Бобом:



Использование элемента shadow позволило Алисе встроить виджет Боба в свой без необходимости вносить изменения в работающий код. «Боб, конечно, молодец, что придумал такой простой способ сохранить разметку чистой, но вся выгода от этого досталась мне», — улыбаясь, подумала Алиса.

Ссылки

[CSS21]
Bert Bos; Tantek Celik; Ian Hickson; Hakon Wium Lie et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 7 June 2011. W3C Recommendation.
[CSS3UI]
Tantek Celik; Florian Rivoal. CSS Basic User Interface Module Level 3 (CSS3 UI). 7 July 2015. W3C Candidate Recommendation.
[CSSOM-VIEW]
Simon Pieters; Glenn Adams. CSSOM View Module. 17 December 2013. W3C Working Draft.
[DOM]
Anne van Kesteren; Aryeh Gregor; Ms2ger; Alex Russell; Robin Berjon. W3C DOM4. 18 June 2015. W3C Last Call Working Draft.
[DOM-Level-3-Events]
Gary Kacmarcik; Travis Leithead. UI Events (formerly DOM Level 3 Events). 28 April 2015. W3C Working Draft.
[DOM-PARSING]
Travis Leithead. DOM Parsing and Serialization. 17 June 2014. W3C Candidate Recommendation.
[EDITING]
Aryeh Gregor. HTML Editing APIs.
[HTML]
Ian Hickson. HTML Standard. Living Standard.
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice.
[SELECTORS4]
Elika Etemad; Tab Atkins Jr… Selectors Level 4. 2 May 2013. W3C Working Draft.
[TOUCH-EVENTS]
Doug Schepers; Sangwhan Moon; Matt Brubeck; Arthur Barstow. Touch Events. 10 October 2013. W3C Recommendation.
[WAI-ARIA]
James Craig; Michael Cooper et al. Accessible Rich Internet Applications (WAI-ARIA) 1.0. 20 March 2014. W3C Recommendation.

Комментарии (2)


  1. Utter_step
    09.07.2015 00:02
    +3

    Спасибо за перевод!


  1. YNile
    10.07.2015 17:19
    +1

    Спасибо!