This specification describes a method of combining multiple DOM trees into one hierarchy and how these trees interact with each other within a document, thus enabling better composition of the DOM.

Shadow DOM specification is being upstreamed to DOM Standard [[WHATWG-DOM]], HTML Standard [[HTML]], CSS Scoping Module Level 1 [[css-scoping-1]], UI Events specification [[uievents]], and other relevant specifications. This specification may not accurately reflect the latest conclusion. See Issue #377 for details.

Conformance

All diagrams, examples, notes, are non-normative, as well as sections explicitly marked as non-normative. Everything else in this specification is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in [[!RFC2119]]. For readability, these words do not appear in all uppercase letters in this specification.

To help with layering and to avoid circular dependencies between various parts of specification, this document consists of three consecutive narratives:

  1. setting up the stage for the specification,
  2. explaining of the conceptual model and algorithms behind it, and
  3. expressing this model with DOM interfaces and HTML elements.

In a sense, these parts can be viewed as math, which sets up the reasoning environment, physics, which is the theoretical reasoning about the concept, and mechanics, which is the practical application of this reasoning.

Any point, at which a conforming UA must make decisions about the state or reaction to the state of the conceptual model, is captured as algorithm. The algorithms are defined in terms of processing equivalence. The processing equivalence is a constraint imposed on the algorithm implementors, requiring the output of the both UA-implemented and the specified algorithm to be exactly the same for all inputs.

Shadow Tree

This section is a copy of Shadow tree in [[WHATWG-DOM]]. This section is expected to be synced with that periodically.

A shadow tree is a node tree whose root is a shadow root.

A shadow root is always attached to another node tree through its host. A shadow tree is therefore never alone. The node tree of a shadow root’s host is sometimes referred to as the light tree.

A shadow tree’s corresponding light tree can be a shadow tree itself.

An element is connected if its shadow-including root is a document.

Slots

A shadow tree contains zero or more elements that are slots.

A slot can only be created through HTML’s slot element.

A slot has an associated name (a string). Unless stated otherwise it is the empty string.

Use these attribute change steps to update a slot’s name:

  1. If element is a slot, localName is name, and namespace is null, then:

    1. If value is oldValue, then return.

    2. If value is null and oldValue is the empty string, then return.

    3. If value is the empty string and oldValue is null, then return.

    4. If value is null or the empty string, then set element’s name to the empty string.

    5. Otherwise, set element’s name to value.

    6. Run assign slotables for a tree with element’s tree.

The first slot in a shadow tree, in tree order, whose name is the empty string, is sometimes known as the "default slot".

A slot has an associated assigned nodes (a list of slotables). Unless stated otherwise it is empty.

Slotables

Element and Text nodes are slotables.

A slot can be a slotable.

A slotable has an associated name (a string). Unless stated otherwise it is the empty string.

Use these attribute change steps to update a slotable’s name:

  1. If localName is slot and namespace is null, then:

    1. If value is oldValue, then return.

    2. If value is null and oldValue is the empty string, then return.

    3. If value is the empty string and oldValue is null, then return.

    4. If value is null or the empty string, then set element’s name to the empty string.

    5. Otherwise, set element’s name to value.

    6. If element is assigned, then run assign slotables for element’s assigned slot.

    7. Run assign a slot for element.

A slotable has an associated assigned slot (null or a slot). Unless stated otherwise it is null. A slotable is assigned if its assigned slot is non-null.

Finding slots and slotables

To find a slot for a given slotable slotable and an optional open flag (unset unless stated otherwise), run these steps:

  1. If slotable’s parent is null, then return null.

  2. Let shadow be slotable’s parent’s shadow root.

  3. If shadow is null, then return null.

  4. If the open flag is set and shadow’s mode is not "open", then return null.

  5. Return the first slot in shadow’s tree whose name is slotable’s name, if any, and null otherwise.

To find slotables for a given slot slot, run these steps:

  1. Let result be an empty list.

  2. If slot’s root is not a shadow root, then return result.

  3. Let host be slot’s root’s host.

  4. For each slotable child of host, slotable, in tree order, run these substeps:

    1. Let foundSlot be the result of finding a slot given slotable.

    2. If foundSlot is slot, then append slotable to result.

  5. Return result.

To find flattened slotables for a given slot slot, run these steps:

  1. Let result be an empty list.

  2. Let slotables be the result of finding slotables given slot.

  3. If slotables is the empty list, then append each slotable child of slot, in tree order, to slotables.

  4. For each node in slotables, run these substeps:

    1. If node is a slot, run these subsubsteps:

      1. Let temporaryResult be the result of finding flattened slotables given node.

      2. Append each slotable in temporaryResult, in order, to result.

    2. Otherwise, append node to result.

  5. Return result.

Assigning slotables and slots

To assign slotables, for a slot slot with an optional suppress signaling flag (unset unless stated otherwise), run these steps:

  1. Let slotables be the result of finding slotables for slot.

  2. If suppress signaling flag is unset, and slotables and slot’s assigned nodes are not identical, then run signal a slot change for slot.

  3. Set slot’s assigned nodes to slotables.

  4. For each slotable in slotables, set slotable’s assigned slot to slot.

To assign slotables for a tree, given a tree tree and an optional set of slots noSignalSlots (empty unless stated otherwise), run these steps for each slot slot in tree, in tree order:

  1. Let suppress signaling flag be set, if slot is in noSignalSlots, and unset otherwise.

  2. Run assign slotables for slot with suppress signaling flag.

To assign a slot, given a slotable slotable, run these steps:

  1. Let slot be the result of finding a slot with slotable.

  2. If slot is non-null, then run assign slotables for slot.

Signaling slot change

Each unit of related similar-origin browsing contexts has a signal slot list (a list of slots). Unless stated otherwise it is empty. [HTML]

To signal a slot change, for a slot slot, run these steps:

  1. If slot is not in unit of related similar-origin browsing contexts' signal slot list, append slot to unit of related similar-origin browsing contexts' signal slot list.

  2. Queue a mutation observer compound microtask.

Mutation algorithms

This section is a copy of Mutation algorithms in [[WHATWG-DOM]]. This section is expected to be synced with that periodically.

To ensure pre-insertion validity of a node into a parent before a child, run these steps:

  1. If parent is not a Document, DocumentFragment, or Element node, throw a HierarchyRequestError.
  2. If node is a host-including inclusive ancestor of parent, throw a HierarchyRequestError.
  3. If child is not null and its parent is not parent, then throw a NotFoundError.
  4. If node is not a DocumentFragment, DocumentType, Element, Text, ProcessingInstruction, or Comment node, throw a HierarchyRequestError.
  5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, throw a HierarchyRequestError.
  6. If parent is a document, and any of the statements below, switched on node, are true, throw a HierarchyRequestError.
    DocumentFragment node
    If node has more than one element child or has a Text node child.

    Otherwise, if node has one element child and either parent has an element child, child is a doctype, or child is not null and a doctype is following child.

    element
    parent has an element child, child is a doctype, or child is not null and a doctype is following child.
    doctype
    parent has a doctype child, child is non-null and an element is preceding child, or child is null and parent has an element child.

To pre-insert a node into a parent before a child, run these steps:

  1. Ensure pre-insertion validity of node into parent before child.
  2. Let reference child be child.
  3. If reference child is node, set it to node’s next sibling.
  4. Adopt node into parent’s node document.
  5. Insert node into parent before reference child.
  6. Return node.

Specifications may define insertion steps for all or some nodes. The algorithm is passed insertedNode, as indicated in the insert algorithm below.

To insert a node into a parent before a child, with an optional suppress observers flag, run these steps:

  1. Let count be the number of children of node if it is a DocumentFragment node, and one otherwise.
  2. If child is non-null, run these substeps:
    1. For each range whose start node is parent and start offset is greater than child’s index, increase its start offset by count.
    2. For each range whose end node is parent and end offset is greater than child’s index, increase its end offset by count.
  3. Let nodes be node’s children if node is a DocumentFragment node, and a list containing solely node otherwise.
  4. If node is a DocumentFragment node, remove its children with the suppress observers flag set.
  5. If node is a DocumentFragment node, queue a mutation record of "childList" for node with removedNodes nodes.

    This step intentionally does not pay attention to the suppress observers flag.

  6. For each node in nodes, in tree order, run these substeps:

    1. Insert node into parent before child or at the end of parent if child is null.

    2. If parent is a shadow host and node is a slotable, then assign a slot for node.

    3. If parent is a slot whose assigned nodes is the empty list, then run signal a slot change for parent.

    4. Run assign slotables for a tree with node’s tree and a set containing each inclusive descendant of node that is a slot.

    5. For each shadow-including inclusive descendant inclusiveDescendant of node, in shadow-including tree order, run these subsubsteps:

      1. Run the insertion steps with inclusiveDescendant.

      2. If inclusiveDescendant is connected, then:

        1. If inclusiveDescendant is custom, then enqueue a custom element callback reaction with inclusiveDescendant, callback name "connectedCallback", and an empty argument list.

        2. Otherwise, try to upgrade inclusiveDescendant.

          If this successfully upgrades inclusiveDescendant, its connectedCallback will be enqueued automatically during the upgrade an element algorithm.

  7. If suppress observers flag is unset, queue a mutation record of "childList" for parent with addedNodes nodes, nextSibling child, and previousSibling child’s previous sibling or parent’s last child if child is null.

To append a node to a parent, pre-insert node into parent before null.

To replace a child with node within a parent, run these steps:

  1. If parent is not a Document, DocumentFragment, or Element node, throw a HierarchyRequestError.
  2. If node is a host-including inclusive ancestor of parent, throw a HierarchyRequestError.
  3. If child’s parent is not parent, then throw a NotFoundError.
  4. If node is not a DocumentFragment, DocumentType, Element, Text, ProcessingInstruction, or Comment node, throw a HierarchyRequestError.
  5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, throw a HierarchyRequestError.
  6. If parent is a document, and any of the statements below, switched on node, are true, throw a HierarchyRequestError.
    DocumentFragment node
    If node has more than one element child or has a Text node child.

    Otherwise, if node has one element child and either parent has an element child that is not child or a doctype is following child.

    element
    parent has an element child that is not child or a doctype is following child.
    doctype
    parent has a doctype child that is not child, or an element is preceding child.

    The above statements differ from the pre-insert algorithm.

  7. Let reference child be child’s next sibling.
  8. If reference child is node, set it to node’s next sibling.
  9. Let previousSibling be child’s previous sibling.

  10. Adopt node into parent’s node document.
  11. Let removedNodes be the empty list.
  12. If child’s parent is not null, run these substeps:

    1. Set removedNodes to a list solely containing child.

    2. Remove child from its parent with the suppress observers flag set.

    The above can only be false if child is node.

  13. Let nodes be node’s children if node is a DocumentFragment node, and a list containing solely node otherwise.
  14. Insert node into parent before reference child with the suppress observers flag set.
  15. Queue a mutation record of "childList" for target parent with addedNodes nodes, removedNodes removedNodes, nextSibling reference child, and previousSibling previousSibling.
  16. Return child.

To replace all with a node within a parent, run these steps:

  1. If node is not null, adopt node into parent’s node document.
  2. Let removedNodes be parent’s children.
  3. Let addedNodes be the empty list if node is null, node’s children if node is a DocumentFragment node, and a list containing node otherwise.
  4. Remove all parent’s children, in tree order, with the suppress observers flag set.
  5. If node is not null, insert node into parent before null with the suppress observers flag set.
  6. Queue a mutation record of "childList" for parent with addedNodes addedNodes and removedNodes removedNodes.

This algorithm does not make any checks with regards to the node tree constraints. Specification authors need to use it wisely.

To pre-remove a child from a parent, run these steps:

  1. If child’s parent is not parent, then throw a NotFoundError.
  2. Remove child from parent.
  3. Return child.

Specifications may define removing steps for all or some nodes. The algorithm is passed removedNode, and optionally oldParent, as indicated in the remove algorithm below.

To remove a node from a parent, with an optional suppress observers flag, run these steps:

  1. Let index be node’s index.
  2. For each range whose start node is an inclusive descendant of node, set its start to (parent, index).
  3. For each range whose end node is an inclusive descendant of node, set its end to (parent, index).
  4. For each range whose start node is parent and start offset is greater than index, decrease its start offset by one.
  5. For each range whose end node is parent and end offset is greater than index, decrease its end offset by one.
  6. For each NodeIterator object iterator whose root’s node document is node’s node document, run the NodeIterator pre-removing steps given node and iterator.

  7. Let oldPreviousSibling be node’s previous sibling.
  8. Let oldNextSibling be node’s next sibling.
  9. Remove node from its parent.
  10. If node is assigned, then run assign slotables for node’s assigned slot.

  11. If parent is a slot whose assigned nodes is the empty list, then run signal a slot change for parent.

  12. If node has an inclusive descendant that is a slot, then:

    1. Run assign slotables for a tree with parent’s tree.

    2. Run assign slotables for a tree with node’s tree and a set containing each inclusive descendant of node that is a slot.

  13. Run the removing steps with node and parent.

  14. If node is custom, then enqueue a custom element callback reaction with node, callback name "disconnectedCallback", and an empty argument list.

    It is intentional for now that custom elements do not get parent passed. This might change in the future if there is a need.

  15. For each shadow-including descendant descendant of node, in shadow-including tree order, run these substeps:

    1. Run the removing steps with descendant.

    2. If descendant is custom, then enqueue a custom element callback reaction with descendant, callback name "disconnectedCallback", and an empty argument list.

  16. For each inclusive ancestor inclusiveAncestor of parent, if inclusiveAncestor has any registered observers whose options' subtree is true, then for each such registered observer registered, append a transient registered observer whose observer and options are identical to those of registered and source which is registered to node’s list of registered observers.
  17. If suppress observers flag is unset, queue a mutation record of "childList" for parent with removedNodes a list solely containing node, nextSibling oldNextSibling, and previousSibling oldPreviousSibling.

Events

This section is a copy of Dispatching events section in [[WHATWG-DOM]]. This section is expected to be synced with that periodically.

Dispatching events

To dispatch an event to a target, with an optional legacy target override flag, run these steps:

  1. Set event’s dispatch flag.

  2. Let targetOverride be target, if legacy target override flag is not given, and target’s associated Document otherwise. [HTML]

    legacy target override flag is only used by HTML and only when target is a Window object.

  3. Let relatedTarget be the result of retargeting event’s relatedTarget against target if event’s relatedTarget is non-null, and null otherwise.
  4. If target is relatedTarget and target is not event’s relatedTarget, then return true.

  5. Append (target, targetOverride, relatedTarget) to event’s path.

  6. Let isActivationEvent be true, if event is a MouseEvent object and event’s type attribute is "click", and false otherwise.

  7. Let activationTarget be target, if isActivationEvent is true and target has activation behavior, and null otherwise.

  8. Let parent be the result of invoking target’s get the parent with event.

  9. While parent is non-null:

    1. Let relatedTarget be the result of retargeting event’s relatedTarget against parent if event’s relatedTarget is non-null, and null otherwise.

    2. If target’s root is a shadow-including inclusive ancestor of parent, then:

      1. If isActivationEvent is true, event’s bubbles attribute is true, activationTarget is null, and parent has activation behavior, then set activationTarget to parent.

      2. Append (parent, null, relatedTarget) to event’s path.

    3. Otherwise, if parent and relatedTarget are identical, then set parent to null.

    4. Otherwise, set target to parent and then:

      1. If isActivationEvent is true, activationTarget is null, and target has activation behavior, then set activationTarget to target.

      2. Append (parent, target, relatedTarget) to event’s path.

    5. If parent is non-null, then set parent to the result of invoking parent’s get the parent with event.

  10. Set event’s eventPhase attribute to CAPTURING_PHASE.

  11. If activationTarget is non-null and activationTarget has legacy-pre-activation behavior, then run activationTarget’s legacy-pre-activation behavior.

  12. For each tuple in event’s path, in reverse order:

    1. Set event’s target attribute to the target of the last tuple in event’s path, that is either tuple or preceding tuple, whose target is non-null.

    2. Set event’s relatedTarget to tuple’s relatedTarget.

    3. Run the retargeting steps with event.

    4. If tuple’s target is null, then invoke tuple’s item with event.

  13. For each tuple in event’s path, in order:

    1. Set event’s target attribute to the target of the last tuple in event’s path, that is either tuple or preceding tuple, whose target is non-null.

    2. Set event’s relatedTarget to tuple’s relatedTarget.

    3. Run the retargeting steps with event.

    4. If tuple’s target is non-null, then set event’s eventPhase attribute to AT_TARGET.

    5. Otherwise, set event’s eventPhase attribute to BUBBLING_PHASE.

    6. If either event’s eventPhase attribute is BUBBLING_PHASE and event’s bubbles attribute is true or event’s eventPhase attribute is AT_TARGET, then invoke tuple’s item with event.

  14. Unset event’s dispatch flag, stop propagation flag, and stop immediate propagation flag.

  15. Set event’s eventPhase attribute to NONE.

  16. Set event’s currentTarget attribute to null.

  17. Set event’s path to the empty list.

  18. If activationTarget is non-null, then:

    1. If event’s canceled flag is unset, then run activationTarget’s activation behavior with event.

    2. Otherwise, if activationTarget has legacy-canceled-activation behavior, then run activationTarget’s legacy-canceled-activation behavior.

  19. Return false if event’s canceled flag is set, and true otherwise.

User Interaction

Ranges and Selections

Selection [[!EDITING]] is not defined. Implementation should do their best to do what's best for them. Here's one possible, admittedly naive way:

Since nodes which are in the different node trees never have the same root, there may never exist a valid DOM range that spans multiple node trees.

Accordingly, selections may only exist within one node tree, because they are defined by a single range. The selection, returned by the window.getSelection() method never returns a selection within a shadow tree.

The getSelection() method of the shadow root object returns the current selection in this shadow tree.

Focus

A shadow host can delegate focus to its shadow root by assigning a boolean delegatesFocus flag to be true in ShadowRootInit dictionary. If omitted, a shadow host does not delegate focus to its shadow root, and the shadow host itself can be focusable.

When a shadow host HOST delegates focus, user agent must behave as follows.

  1. In sequential focus navigation, HOST itself will be skipped. See the next secition for the formal definition.
  2. When HOST is focused by focus() method or autofocus attribute: The first focusable area in focus navigation order of HOST's shadow root's focus navigation scope gets focus. See the next section for the formal definition of the ordering.
  3. When mouse is clicked on a node in HOST's shadow tree and the node is not a focusable area: The first focusable area in focus navigation order of HOST's shadow root's focus navigation scope gets focus. See the next section for the formal definition of the ordering.
  4. When any element in HOST's shadow tree has focus, :focus pseudo-class applies to HOST in addition to the focused element itself.
  5. If :focus pseudo-class applies to HOST, and HOST is in a shadow root of another shadow host HOST2 which also delegates focus, :focus pseudo-class applies to HOST2 as well.

Sequential Focus Navigation

focus navigation scope is a set of elements in a shadow-including document. An element belongs to at most one focus navigation scope.

focus navigation scope owner is a node that owns a focus navigation scope. A document, a shadow root, or a slot element can be a focus navigation scope owner.

document sequential focus navigation order is an order of all of the focusable areas in a shadow-including document, reachable by sequential focus navigation.

When a shadow-including document is given, the document sequential focus navigation order is determined by the following steps:

Step 1.

INPUT
DOCUMENT, a composed document
OUTPUT
OWNERS, a set of focus navigation scope owner nodes
  1. Let OWNERS be an empty set.
  2. For each node tree TREE in DOCUMENT:
    1. For each node NODE in TREE, in tree order:
      1. If NODE is a document, a shadow root, or a slot element, append it to OWNERS.

Step 2.

INPUT
DOCUMENT, a composed document
OWNERS, a set of focus navigation scope owner nodes from Step 1
OUTPUT
SCOPEs, a set of focus navigation scopes each of which is an ordered list of elements
SCOPE-MAP, a map from each focus navigation scope owner to its corresponding focus navigation scope
  1. For each owner OWNER in OWNERS:
    1. Let SCOPE be empty list.
    2. Append SCOPE to SCOPE-MAP[OWNER].
  2. For each node tree TREE in DOCUMENT, in shadow-including tree order:
    1. For each ELEMENT in TREE:
      1. Let CURRENT be ELEMENT.
      2. Repeat the following steps until the algorithm stops.
      3. If CURRENT is a child of a shadow host:
        1. If CURRENT is assigned to any slot SLOT, append ELEMENT to SCOPE-MAP[SLOT].
        2. Otherwise do not append ELEMENT to any scope.
        3. Stop this algorithm.
      4. If CURRENT is a slot element and not equal to ELEMENT:
        1. If CURRENT has any assigned nodes, do not append ELEMENT to any scope.
        2. Otherwise append ELEMENT to SCOPE-MAP[CURRENT].
        3. Stop this algorithm.
      5. If CURRENT is a child of a root node (a shadow root or document) ROOT:
        1. Append ELEMENT to SCOPE-MAP[ROOT].
        2. Stop this algorithm.
      6. Otherwise:
        1. Set CURRENT to the parent element of CURRENT.

Step 3.

INPUT
SCOPE, a focus navigation scope
OUTPUT
NAVIGATION-ORDER, an ordered list of elements which should be visited.
  1. For each scope SCOPE:
    1. Let NAVIGATION-ORDER be an empty list.
    2. For each element ELEMENT in a focus navigation scope SCOPE,
      1. if ELEMENT is focusable, a shadow host, or a slot element, append ELEMENT to NAVIGATION-ORDER.
    3. Reorder NAVIGATION-ORDER according to the tabindex value attached to each node. In this step, an element whose tabindex value is negative must not be appended to NAVIGATION-ORDER.

Step 4.

Apply the following focus navigation order merging algorithm with document’s focus navigation order as input. The result is the document sequential focus navigation order.

INPUT
NAVIGATION-ORDER, an ordered list of elements.
OUTPUT
MERGED-NAVIGATION-ORDER, an ordered list of elements.
  1. Let MERGED-NAVIGATION-ORDER be an empty list.
  2. For each element ELEMENT in NAVIGATION-ORDER:
    1. If ELEMENT is a shadow host:
      1. Unless its shadow root’s delegatesFocus flag is set, append ELEMENT to MERGED-NAVIGATION-ORDER.
      2. Apply the focus navigation order merging algorithm with the focus navigation order owned by its shadow root as input, then append the result to MERGED-NAVIGATION-ORDER.
    2. If ELEMENT is a slot element:
      1. Apply the focus navigation order merging algorithm with the focus navigation order owned by the slot element as input, then append the result to MERGED-NAVIGATION-ORDER.
    3. Otherwise:
      1. Append ELEMENT to MERGED-NAVIGATION-ORDER.
    4. Return MERGED-NAVIGATION-ORDER.

For directional focus navigation [[!CSS3-UI]], it is up to the user agent to integrate the shadow trees into the document's directional focus navigation.

Active Element

DocumentOrShadowRoot object's activeElement must be the result of the retargeting algorithm with the context object and the focused element as input, if the result and the context object are in the same tree. Otherwise, null.

Editing

The value of the contenteditable attribute must not propagate from shadow host to its shadow trees.

Assistive Technology

User agents with assistive technology traverse the flat tree, and thus enable full use of WAI-ARIA [[!WAI-ARIA]] semantics in the shadow trees.

Hit Testing

When a text node is a child node of a shadow root, a hit testing must target the shadow host if the text node is the result of the hit testing.

User-agent mouse events must be targeted to the parent node in the flat tree of a text node if the topmost event target is the text node.

This section eventually needs to be part of some general hit testing specification.

HTML Elements in Shadow Trees

Inertness of HTML Elements in a shadow tree

Comparatively, a shadow tree can be seen as somewhere between just part of a document and itself being a document fragment. Since it is rendered, a shadow tree aims to retain the traits of a typical tree in a document. At the same time, it is an encapsulation abstraction, so it has to avoid affecting the document tree. Thus, the HTML elements must behave as specified [[!HTML]] in the shadow trees, with a few exceptions.

According to the [[!HTML]], some HTML Elements would have different behavior if they participate in a shadow tree, instead of a document tree, because their definitions require the elements to be in a document as a necessary condition for them to work. In other words, they shouldn't work if they participate in a shadow tree, even when they are in a shadow-including document. We must fill this gap because we expect that most of HTML Elements behave in the same way as in a document, as long as they are in a shadow-including document. See W3C Bug 26365 and Bug 27406 for the details. The following is the tentative summary of the discussions in the W3C bugs. We, however, haven't covered all HTML Elements and their behaviors here yet. For HTML Elements which are not explicitly stated here, they should be considered as active in a shadow tree. We are trying to update [[!HTML]] itself, instead of having monkey patches here.

HTML Elements are classified into the following categories:

Attributes

When [[!HTML]] defines the processing algorithms to traverse trees for the following attributes, they must use the flat tree.

This list does not include attributes that are defined elsewhere in this specification. Such attributes include:

Other Clarifications

This section is used to state what needs to be clarified. Each clarification will be upstreamed to the HTML Standard or other specifications, eventually, if required.

Document.currentScript must return null if the script element is in a shadow tree. See Issue #477.

Style elements inside a shadow tree must not be able to set the preferred style sheet set for the document tree. Style elements inside a shadow tree should still be applied if it has a title attribute not matching the preferred style sheet set of the document tree. See Issue #391.

An iframe in a shadow tree must not have any effect on window.history neither window.frames. See Issue #184.

:root pseudo class does not match any element if the rule is used in a shadow tree.

Elements and DOM interfaces

Extensions to the DocumentOrShadowRoot Mixin

          partial interface DocumentOrShadowRoot {
            Selection? getSelection ();
            Element? elementFromPoint(double x, double y);
            sequence<Element> elementsFromPoint(double x, double y);
            CaretPosition? caretPositionFromPoint(double x, double y);
            readonly attribute Element? activeElement;
            readonly attribute StyleSheetList styleSheets;
          };
        

The ShadowRoot interface

The ShadowRoot interface represents the shadow root.

readonly attribute Element host

Represents the shadow host which hosts the context object.

On getting, the attribute must return the shadow host which hosts the context object.

[TreatNullAs=EmptyString] attribute DOMString innerHTML
This is similarly defined in Element's innerHTML with the following exceptions:

Because DocumentFragment does not always have a host, innerHTML can not be defined in DocumentFragment.

The nodeType attribute of a ShadowRoot instance must return DOCUMENT_FRAGMENT_NODE. Accordingly, the nodeName attribute of a ShadowRoot instance must return "#document-fragment".

Extensions to Element Interface

ShadowRoot attachShadow(ShadowRootInit shadowRootInitDict)
When invoked, these steps must be run:
  1. If the context object is none of the following elements, throw a NotSupportedError exception.
    • custom element
    • article
    • aside
    • blockquote
    • body
    • div
    • footer
    • h1
    • h2
    • h3
    • h4
    • h5
    • h6
    • header
    • nav
    • p
    • section
    • span

    This list is possible to change. See Issue #110.

  2. If the context object already hosts the shadow tree, throws InvalidStateError exception.
  3. Create a new instance of the ShadowRoot object. The shadowRootInitDict argument allows for setting the mode.
  4. Let the context object host the ShadowRoot object.
  5. Return ShadowRoot object.
readonly attribute HTMLSlotElement? assignedSlot

Represents the assigned slot of the context object.

On getting, the attribute must return the assigned slot of the context object, if there is, and the assigned slot's root's mode is "open". Otherwise must return null.

attribute DOMString slot

Reflects the slot attribute. The slot attribute represents the slot name of a slot to where this element is assigned.

readonly attribute ShadowRoot? shadowRoot

Represents the shadow root that context object hosts.

On getting, the attribute must return the shadow root that context object hosts if there is and its mode is "open". Otherwise must return null.

ShadowRootInit dictionary

required ShadowRootMode mode
Specifies the associated mode of ShadowRoot
boolean delegatesFocus = false
Specifies whether shadow host delegates focus to its ShadowRoot. If omitted, the default value is false.

ShadowRootMode enum

open
Specifies "open" mode
closed
Specifies "closed" mode

Extensions to Text Interface

readonly attribute HTMLSlotElement? assignedSlot

Represents the assigned slot of the context object.

On getting, the attribute must return the assigned slot of the context object, if there is, and the assigned slot participates in an open shadow tree. Otherwise must return null.

The slot element

The slot element is used to define a location of a slot.

Context
Where flow content is expected.
Content model
Transparent
Children
Anything as fallback content
Content attributes
Global attributes
name, the slot name
Represents the slot name.
DOM Interface
attribute DOMString name
Must reflect the name attribute.
sequence<Node> assignedNodes(optional AssignedNodesOptions options)
When invoked, it must return a sequence consisting of either the assigned nodes or the distributed nodes of the context object. If the optional options is given and its flatten is true, the distributed nodes must be returned. Otherwise, the assigned nodes must be returned.

AssignedNodesOptions dictionary

boolean flatten = false

Specifies whether assignedNodes() returns assigned nodes or distributed nodes

Extensions to EventInit Dictionary

boolean scoped = false

Specifies the scoped flag of Event

Extensions to Event Interface

sequence<EventTarget> deepPath()

When invoked, it must return return a sequence consisting of event targets, that must be equivalent to processing the following steps:

Input
EVENT, a context object
Output
FILTERED-PATH, an array of event targets
  1. If EVENT has not been dispatched or EVENT is no longer dispatched, let FILTERED-PATH be an empty array.
  2. Otherwise:
    1. Let PATH be the Event's event path.
    2. Let FILTERED-PATH be the empty ordered list.
    3. Let CURRENT-TARGET-NODE be the result of find the node algorithm with EVENT's current target and PATH.
    4. For each TARGET in PATH:
      1. Let TARGET-NODE be the result of find the Node algorithm with TARGET and PATH.
      2. If both CURRENT-TARGET-NODE and TARGET-NODE exist,
        1. If TARGET-NODE is an unclosed node of the CURRENT-TARGET-NODE, append TARGET to FILTERED-PATH. Otherwise, do nothing
      3. Otherwise:
        1. append TARGET to FILTERED-PATH.

The find the node algorithm given a EVENT-TARGET and PATH are as follows:

  1. For each ITEM in the sub list of PATH, from EVENT-TARGET, inclusive, to the last item of PATH:
    1. If ITEM is a Node, return ITEM.
  2. Return null.
readonly attribute boolean scoped

Returns the scoped flag.

Shadow DOM Example

Bob was asked to turn a simple list of links into a News Widget, which has links organized into two categories: breaking news and just news. The current document markup for the stories looks like this:

<ul class="stories">
    <li><a href="//example.com/stories/1">A story</a></li>
    <li><a href="//example.com/stories/2">Another story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/3">Also a story</a></li>
    <li><a href="//example.com/stories/4">Yet another story</a></li>
    <li><a href="//example.com/stories/5">Awesome story</a></li>
    <li class="breaking" slot="breaking"><a href="//example.com/stories/6">Horrible story</a></li>
</ul>
      

It's weird that there are slot attributes in this markup because Bob has not decided to use Shadow DOM yet.

To organize the stories, Bob decides to use shadow DOM. Doing so will allow Bob to keep the document markup uncluttered, and harnessing the power of insertion point makes sorting stories by class name a very simple task. After getting another cup of Green Eye, he quickly mocks up the following shadow tree, to be hosted by the ul element:

<div class="breaking">
    <ul>
        <slot name="breaking"></slot> <!-- slot for breaking news -->
    </ul>
</div>
<div class="other">
    <ul>
        <slot></slot> <!-- slot for the rest of the news -->
    </ul>
</div>
      

Bob then styles the newborn widget according to comps from the designer by adding this to the shadow tree mockup:

<style>
    div.breaking {
        color: Red;
        font-size: 20px;
        border: 1px dashed Purple;
    }
    div.other {
        padding: 2px 0 0 0;
        border: 1px solid Cyan;
    }
</style>
      

While pondering if his company should start looking for a new designer, Bob converts the mockup to code:

function createStoryGroup(className, slotName)
{
    var group = document.createElement('div');
    group.className = className;
    // Empty string in slot name attribute or absence thereof work the same, so no need for special handling.
    group.innerHTML = '<ul><slot name="' + slotName + '"></slot></ul>';
    return group;
}

function createStyle()
{
    var style = document.createElement('style');
    style.textContent = 'div.breaking { color: Red;font-size: 20px; border: 1px dashed Purple; }' +
        'div.other { padding: 2px 0 0 0; border: 1px solid Cyan; }';
    return style;
}

function makeShadowTree(storyList)
{
    var root = storyList.attachShadow({mode: 'open'});
    root.appendChild(createStyle());
    root.appendChild(createStoryGroup('breaking', 'breaking'));
    root.appendChild(createStoryGroup('other', ''));
}

document.addEventListener('DOMContentLoaded', function() {
    document.querySelectorAll('ul.stories').forEach(makeShadowTree);
});
      

Well done, Bob! With the cup of coffee still half-full, the work is complete. Recognizing his awesomeness, Bob returns to teaching n00bs the ways of Splatoon.

Acknowledgements

David Hyatt developed XBL 1.0, and Ian Hickson co-wrote XBL 2.0. These documents provided tremendous insight into the problem of functional encapsulation and greatly influenced this specification.

Alex Russell and his considerable forethought triggered a new wave of enthusiasm around the subject of shadow DOM and how it can be applied practically on the Web.

Dominic Cooney, Hajime Morrita, and Roland Steiner worked tirelessly to scope the problem of functional encapsulation within the confines of the Web platform and provided a solid foundation for this document.

The editor would also like to thank Alex Komoroske, Anne van Kesteren, Brandon Payton, Brian Kardell, Darin Fisher, Eric Bidelman, Deepak Sherveghar, Edward O'Connor, Elisée Maurer, Elliott Sprehn, Erik Arvidsson, Glenn Adams, Jonas Sicking, Koji Ishii, Malte Ubl, Mike Taylor, Oliver Nightingale, Olli Pettay, Rafael Weinstein, Richard Bradshaw, Ruud Steltenpool, Sam Dutton, Sergey G. Grekhov, Shinya Kawanaka, Tab Atkins, Takashi Sakamoto, and Yoshinori Sano for their comments and contributions to this specification.

This list is too short. There's a lot of work left to do. Please contribute by reviewing and filing bugs—and don't forget to ask the editor to add your name into this section.