├── .gitignore ├── README ├── appendix.html ├── binding.html ├── dom.html ├── events.html ├── index.html ├── introduction.html ├── rendering.html ├── resources ├── related-target-and-shadow-dom.svg └── styles.css ├── styles.html └── templates.html /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Chicken-scratch and deep thoughts, mixed together into a delicious Spec Pie. 2 | -------------------------------------------------------------------------------- /appendix.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /binding.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dom.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 |
An shadow root is represented by the ShadowRoot object: 8 | 9 |
[Constructor(in HTMLElement element) raises(DOMException)]
10 | interface ShadowRoot : TreeScope {
11 | attribute bool applyAuthorSheets;
12 | readonly attribute Element shadowHost;
13 | }
14 |
15 |
16 | The element
parameter in ShadowRoot constructor is an Element that will be hosting this instance. If the element is already hosting a shadow DOM subtree, a NO_MODIFICATION_ALLOWED_ERR DOM Exception is raised.
17 |
18 |
The applyAuthorSheets attribute indicates whether or not rules in author style sheets associated with the element's document apply to the shadow DOM subtree. It is an equivalent of the XBL2 apply-author-sheets attribute.
19 | 20 |The shadowHost attribute refers back to the element that is hosting this shadow root.
21 | 22 |A ShadowRoot is a TreeScope, which represents a DOM tree scope.
23 |
24 | The parentTreeScope attribute returns the tree scope that contains this tree scope. The getElementById returns first element, in tree order, within the tree scope's tree, whose ID is The component model extends the Element as follows: The pseudo attribute allows setting a CSS pseudo-element value on an element and roughly corresponds to functionality of the XBL2 pseudo attribute.
43 |
44 |
47 |
48 | The value must either be When an event is fired in a shadow DOM subtree, it either escapes out of the shadow tree during the capture, target, bubble, or default phases or it is stopped at the shadow boundary. To help with algorithm description, let's define the direction walking the ancestors of a node as up when traversing from child to parent, and down when traversing from parent to child. In the context of events-related algorithms, all ancestor traversal crosses shadow boundaries. In the cases where event escapes the shadow tree, the event is retargeted to ensure that the original target-related information on the event object does not reveal information about the shadow DOM subtree beyond the boundary. Event retargeting is a process of computing a relative target for each ancestor of the node at which the event is dispatched. The following retargeting algorithm must be used to determine relative targets: The retargeting process must occur prior to dispatch of an event. At the time of event dispatch, for capturing, target, and bubbling phases, The event must not be retargeted during the default phase of dispatch. Upon completion of the event dispatch, the Some events have a Thus, if an event has a The following algorithm must be used: The Mutation events and Suppose we have a user interface for a media controller, built with several components, represented by this shadow-aware DOM tree: Let's have a user position their pointing device over the volume slider's thumb ( Just before the event is dispatched, we perform retargeting:
122 | At the end of this process, we should have the following set of ancestors and relative targets: After we dispatch the Let's study how the To be defined: To be defined:
29 |
30 | interface TreeScope : Node {
25 | readonly TreeScope parentTreeScope;
26 | Element getElementById(in DOMString elementId);
27 | }
28 |
elementId
. It is essentially document.getElementById from DOM Core, but scoped inside of the tree scope.
41 |
42 | [Supplemental]
37 | interface Element {
38 | attribute String pseudo;
39 | }
40 |
null
or a string that matches the IDENT
production of the CSS grammar, with the exception that the names defined by CSS for pseudo class selectors and pseudo element selectors are not valid, for example: active
, after
, before
, checked
, disabled
, empty
, enabled
, first-child
, first-letter
, first-line
, first-of-type
, focus
, hover
, indeterminate
, lang
, last-child
, last-of-type
, link
, not
, nth-child
, nth-last-child
, nth-last-of-type
, nth-of-type
, only-child
, only-of-type
, root
, target
, visited
.
49 |
--------------------------------------------------------------------------------
/events.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
29 |
30 | Event Retargeting
31 |
32 |
42 |
51 |
45 |
49 | Event
object's target
and currentTarget
must be set to the relative target that was computed for the node along the capturing and bubbling flow.Event
object's target
and currentTarget
are set to the highest ancestor's relative target. Since it is possible for a script to hold on to the Event
object past the scope of event dispatch, this step is necessary to avoid revealing the nodes in shadow DOM subtrees.Related Target Events
62 |
63 | relatedTarget
property, which holds a node that's not the event's target, but is related to the event. For instance, a mouseover
event's relatedTarget
may hold the node from which the mouse has moved to event's target
. In the case where relatedTarget
is in a shadow DOM subtree, we don't want to leak its actual value outside of this subtree. In cases where both relatedTarget
and target
are part of the same shadow DOM subtree, we don't want the appearance of spurious mouseover
and mouseout
events firing from the same node.relatedTarget
, its value and extent of event dispatch must to be adjusted. In general:
66 |
67 |
70 | relatedTarget
and target
are enclosed by a shadow DOM subtree, the event dispatch must be stopped at the shadow boundary of this subtree.relatedTarget
is separated from target
by one or more shadow boundaries, it must be retargeted to the boundary that's closest to target
.
72 |
74 |
85 | target
and relatedTarget
.relatedTarget
, and it's a shadow DOM subtree host, make this tree's boundary the lowest common boundary, and go to step 6 in the algorithm.target
and relatedTarget
.
78 | relatedTarget
.relatedTarget
, find a shadow boundary. This is the first divergent boundary.relatedTarget
to the parent of the first divergent boundary.
84 | Focus Events
88 |
89 | focus
, DOMFocusIn
, blur
, and DOMFocusOut
events must be treated in the same way as events with a relatedTarget
, where the corresponding node that is losing focus as a result of target
gaining focus or the node that is gaining focus, and thus causing the blurring of target
acts as the related target.
90 |
91 | Events That Are Always Stopped
92 |
93 | selectstart
event are always stopped at the nearest shadow boundary.Example
96 |
97 |
118 |
119 | <div id="player">
100 | <shadow-boundary>
101 | <div pseudo="controls">
102 | <button pseudo="play-button">
103 | <input type="range" pseudo="timeline">
104 | <shadow-boundary>
105 | <div pseudo="slider-thumb"></div>
106 | </shadow-boundary>
107 | </input>
108 | <div pseudo="volume-slider-container">
109 | <input type="range" pseudo="volume-slider">
110 | <shadow-boundary>
111 | <div pseudo="slider-thumb"></div>
112 | </shadow-boundary>
113 | </input>
114 | </div>
115 | </div>
116 | </shadow-boundary>
117 | </div>
#player::volume-slider::slider-thumb
), thus triggering a mouseover
event on that node. For this event, let's pretend it has no associated relatedTarget
.
123 |
131 |
132 | #player::volume-slider
), and we set its relative target to itself, since the previous ancestor was a shadow boundary.#player::volume-slider-container
), and use previous ancestor's target -- the input element.#player::controls
), and again use input element as relative target per retargeting algorithm.#player
) and set its relative target to itself.
151 |
152 | <div id="player"> 7
133 | <shadow-boundary> 6
134 | <div pseudo="controls"> 5
135 | <button pseudo="play-button">
136 | <input type="range" pseudo="timeline">
137 | <shadow-boundary>
138 | <div pseudo="slider-thumb"></div>
139 | </shadow-boundary>
140 | </input>
141 | <div pseudo="volume-slider-container"> 4
142 | <input type="range" pseudo="volume-slider"> 3
143 | <shadow-boundary> 2
144 | <div pseudo="slider-thumb"></div> 1
145 | </shadow-boundary>
146 | </input>
147 | </div>
148 | </div>
149 | </shadow-boundary>
150 | </div>
155 |
156 |
188 |
189 |
157 |
160 |
161 |
162 | Ancestor
158 | Relative Target
159 |
163 |
166 |
164 | ...
165 | div#player
167 |
170 |
168 | div#player
169 | div#player
171 |
174 |
172 | div#player::controls
173 | div#player::controls::volume-slider
175 |
178 |
176 | div#player::volume-slider-container
177 | div#player::controls::volume-slider
179 |
182 |
180 | div#player::controls::volume-slider
181 | div#player::controls::volume-slider
183 |
186 |
187 |
184 | div#player::controls::volume-slider::slider-thumb
185 | div#player::controls::volume-slider::slider-thumb
mouseover
event using these newly computed relative targets, the user decides to move their pointing device over the thumb of the timeline (#player::timeline::slider-thumb
). This triggers both a mouseout
event for the volume slider thumb and the mouseover
event for the timeline thumb.relatedTarget
value of the volume thumb's mouseout
event is affected and whether event escapes out of the player. For this event, the relatedTarget
is the timeline thumb (#player::timeline::slider-thumb
).
194 |
200 |
201 | #player::controls
).#player::timeline
). This is the first divergent boundary.relatedTarget
to be the parent of the first divergent boundary, the timeline (#player::timeline
). We are now ready to dispatch this event.
220 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <div id="player">
202 | <shadow-boundary> — lowest common boundary
203 | <div pseudo="controls">
204 | <button pseudo="play-button">
205 | <input type="range" pseudo="timeline">
206 | <shadow-boundary> — first divergent boundary
207 | <div pseudo="slider-thumb"></div>
208 | </shadow-boundary>
209 | </input>
210 | <div pseudo="volume-slider-container">
211 | <input type="range" pseudo="volume-slider">
212 | <shadow-boundary>
213 | <div pseudo="slider-thumb"></div>
214 | </shadow-boundary>
215 | </input>
216 | </div>
217 | </div>
218 | </shadow-boundary>
219 | </div>
Component Model
8 |
9 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/introduction.html:
--------------------------------------------------------------------------------
1 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/rendering.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/resources/related-target-and-shadow-dom.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Helvetica;
3 | line-height: 1.5em;
4 | }
5 |
6 | em.fixme::before {
7 | content: 'FIXME:';
8 | color: White;
9 | background-color: Red;
10 | padding: 0 0.2em;
11 | border-radius: 0.2em;
12 | font-style: normal;
13 | font-weight: bold;
14 | font-size: 0.8em;
15 | }
16 |
17 | dfn {
18 | font-weight: bold;
19 | }
20 |
21 | div.algorithm {
22 | padding: 0.5em 1em;
23 | background-color: LightGray;
24 | }
25 |
26 | span.ref {
27 | border-bottom: 1px dotted Gray;
28 | }
29 |
30 | th, td {
31 | text-align: left;
32 | padding: 5px;
33 | }
34 |
35 | dfn:target {
36 | background: Yellow;
37 | box-shadow: 0 0 25px Yellow;
38 | }
39 |
40 | pre a {
41 | color: Black;
42 | text-decoration: none;
43 | }
44 |
45 | pre a:hover {
46 | text-decoration: underline;
47 | }
48 |
49 | pre {
50 | background-color: LightGray;
51 | padding: 0.5em 1em;
52 | margin-left: 2em;
53 | }
54 |
--------------------------------------------------------------------------------
/styles.html:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/templates.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
:focus
worksnav-index
works