Class: Dommy::ShadowRoot
Overview
‘ShadowRoot` — a DocumentFragment-shaped subtree attached to a host Element via `attachShadow`. Lives in its own Nokogiri fragment that’s invisible to the outer document’s tree walks (querySelector, getElementById, children, etc.), which is the core “encapsulation” the spec promises.
Tree manipulation works the same as a normal Element/Fragment; the boundary is enforced only on outer queries and event composition. CSS scoping (‘:host`, `::slotted`) is out of scope.
Constant Summary
Constants included
from Node
Node::ATTRIBUTE_NODE, Node::CDATA_SECTION_NODE, Node::COMMENT_NODE, Node::DOCUMENT_FRAGMENT_NODE, Node::DOCUMENT_NODE, Node::DOCUMENT_POSITION_CONTAINED_BY, Node::DOCUMENT_POSITION_CONTAINS, Node::DOCUMENT_POSITION_DISCONNECTED, Node::DOCUMENT_POSITION_FOLLOWING, Node::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, Node::DOCUMENT_POSITION_PRECEDING, Node::DOCUMENT_TYPE_NODE, Node::ELEMENT_NODE, Node::PROCESSING_INSTRUCTION_NODE, Node::TEXT_NODE
Instance Attribute Summary collapse
Instance Method Summary
collapse
#__internal_deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener
Constructor Details
#initialize(host, mode:, delegates_focus: false, slot_assignment: "named") ⇒ ShadowRoot
Returns a new instance of ShadowRoot.
21
22
23
24
25
26
27
28
29
|
# File 'lib/dommy/shadow_root.rb', line 21
def initialize(host, mode:, delegates_focus: false, slot_assignment: "named")
@host = host
@mode = mode.to_s
@delegates_focus = !!delegates_focus
@slot_assignment = slot_assignment.to_s
@document = host.document
@__node__ = @document.nokogiri_doc.fragment("")
@document.__internal_register_shadow_fragment__(@__node__, self)
end
|
Instance Attribute Details
#delegates_focus ⇒ Object
Returns the value of attribute delegates_focus.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def delegates_focus
@delegates_focus
end
|
#document ⇒ Object
Returns the value of attribute document.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def document
@document
end
|
#host ⇒ Object
Returns the value of attribute host.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def host
@host
end
|
#mode ⇒ Object
Returns the value of attribute mode.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def mode
@mode
end
|
#slot_assignment ⇒ Object
Returns the value of attribute slot_assignment.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def slot_assignment
@slot_assignment
end
|
Instance Method Details
#[](key) ⇒ Object
‘[]` accessor mirrors the bracket convention used elsewhere.
154
155
156
|
# File 'lib/dommy/shadow_root.rb', line 154
def [](key)
__js_get__(key.to_s)
end
|
#[]=(k, v) ⇒ Object
158
159
160
|
# File 'lib/dommy/shadow_root.rb', line 158
def []=(k, v)
__js_set__(k.to_s, v)
end
|
#__dommy_backend_node__ ⇒ Object
19
|
# File 'lib/dommy/shadow_root.rb', line 19
def __dommy_backend_node__ = @__node__
|
#__internal_event_parent__ ⇒ Object
Event bubbling stops at the ShadowRoot unless event has ‘composed: true`. The host is the bubble-path successor when composition crosses the boundary (handled in Event dispatch).
238
239
240
|
# File 'lib/dommy/shadow_root.rb', line 238
def __internal_event_parent__
nil
end
|
#__js_call__(method, args) ⇒ Object
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
# File 'lib/dommy/shadow_root.rb', line 206
def __js_call__(method, args)
case method
when "querySelector"
query_selector(args[0])
when "querySelectorAll"
query_selector_all(args[0])
when "getElementById"
get_element_by_id(args[0])
when "append"
append(*args)
when "prepend"
prepend(*args)
when "replaceChildren"
replace_children(*args)
when "appendChild"
append_child(args[0])
when "getRootNode"
get_root_node(args[0])
when "contains"
contains?(args[0])
when "addEventListener"
add_event_listener(args[0], args[1], args[2])
when "removeEventListener"
remove_event_listener(args[0], args[1])
when "dispatchEvent"
dispatch_event(args[0])
end
end
|
#__js_get__(key) ⇒ Object
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
# File 'lib/dommy/shadow_root.rb', line 162
def __js_get__(key)
case key
when "host"
@host
when "mode"
@mode
when "delegatesFocus"
@delegates_focus
when "slotAssignment"
@slot_assignment
when "innerHTML"
inner_html
when "textContent"
text_content
when "children"
children
when "childNodes"
child_nodes
when "childElementCount"
child_element_count
when "firstChild"
first_child
when "lastChild"
last_child
when "firstElementChild"
first_element_child
when "lastElementChild"
last_element_child
when "nodeType"
11
end
end
|
#__js_set__(key, value) ⇒ Object
195
196
197
198
199
200
201
202
203
204
|
# File 'lib/dommy/shadow_root.rb', line 195
def __js_set__(key, value)
case key
when "innerHTML"
self.inner_html = value
when "textContent"
self.text_content = value
end
nil
end
|
#append(*args) ⇒ Object
91
92
93
94
95
96
|
# File 'lib/dommy/shadow_root.rb', line 91
def append(*args)
nodes = args.flat_map { |a| detach_dom_nodes(a) }
nodes.each { |n| @__node__.add_child(n) }
@document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: [])
nil
end
|
#append_child(child) ⇒ Object
84
85
86
87
88
89
|
# File 'lib/dommy/shadow_root.rb', line 84
def append_child(child)
nodes = detach_dom_nodes(child)
nodes.each { |n| @__node__.add_child(n) }
@document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: [])
child
end
|
#child_element_count ⇒ Object
64
65
66
|
# File 'lib/dommy/shadow_root.rb', line 64
def child_element_count
@__node__.element_children.size
end
|
#child_nodes ⇒ Object
60
61
62
|
# File 'lib/dommy/shadow_root.rb', line 60
def child_nodes
@__node__.children.map { |n| @document.wrap_node(n) }.compact
end
|
#children ⇒ Object
56
57
58
|
# File 'lib/dommy/shadow_root.rb', line 56
def children
@__node__.element_children.map { |n| @document.wrap_node(n) }.compact
end
|
#contains?(other) ⇒ Boolean
144
145
146
147
148
149
150
151
|
# File 'lib/dommy/shadow_root.rb', line 144
def contains?(other)
return false unless other.respond_to?(:__dommy_backend_node__)
other_node = other.__dommy_backend_node__
return true if other_node == @__node__
Internal::NodeTraversal.ancestor_of?(@__node__, other_node)
end
|
#first_child ⇒ Object
68
69
70
|
# File 'lib/dommy/shadow_root.rb', line 68
def first_child
@document.wrap_node(@__node__.children.first)
end
|
#first_element_child ⇒ Object
76
77
78
|
# File 'lib/dommy/shadow_root.rb', line 76
def first_element_child
@document.wrap_node(@__node__.element_children.first)
end
|
#get_element_by_id(id) ⇒ Object
132
133
134
135
136
|
# File 'lib/dommy/shadow_root.rb', line 132
def get_element_by_id(id)
return nil if id.nil?
@document.wrap_node(@__node__.at_css("##{id}"))
end
|
#get_root_node(_options = nil) ⇒ Object
‘getRootNode()` returns the ShadowRoot itself (closed-shadow semantics; `composed: true` callers go through the Event path).
140
141
142
|
# File 'lib/dommy/shadow_root.rb', line 140
def get_root_node(_options = nil)
self
end
|
#inner_html ⇒ Object
—- Public Ruby API (ParentNode + DocumentFragment mixin) —-
33
34
35
|
# File 'lib/dommy/shadow_root.rb', line 33
def inner_html
@__node__.children.map(&:to_html).join
end
|
#inner_html=(html) ⇒ Object
37
38
39
40
41
42
43
44
45
|
# File 'lib/dommy/shadow_root.rb', line 37
def inner_html=(html)
removed = @__node__.children.to_a
removed.each(&:unlink)
fragment = Parser.fragment(html.to_s, owner_doc: @document.nokogiri_doc)
added = fragment.children.to_a
added.each { |n| @__node__.add_child(n) }
@document.notify_child_list_mutation(target_node: @__node__, added_nodes: added, removed_nodes: removed)
nil
end
|
#last_child ⇒ Object
72
73
74
|
# File 'lib/dommy/shadow_root.rb', line 72
def last_child
@document.wrap_node(@__node__.children.last)
end
|
#last_element_child ⇒ Object
80
81
82
|
# File 'lib/dommy/shadow_root.rb', line 80
def last_element_child
@document.wrap_node(@__node__.element_children.last)
end
|
#prepend(*args) ⇒ Object
98
99
100
101
102
103
104
105
106
107
108
109
|
# File 'lib/dommy/shadow_root.rb', line 98
def prepend(*args)
nodes = args.flat_map { |a| detach_dom_nodes(a) }
anchor = @__node__.children.first
if anchor
nodes.reverse_each { |n| anchor.add_previous_sibling(n) }
else
nodes.each { |n| @__node__.add_child(n) }
end
@document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: [])
nil
end
|
#query_selector(selector) ⇒ Object
120
121
122
123
124
|
# File 'lib/dommy/shadow_root.rb', line 120
def query_selector(selector)
return nil if selector.nil? || selector.to_s.empty?
@document.wrap_node(@__node__.at_css(selector.to_s))
end
|
#query_selector_all(selector) ⇒ Object
126
127
128
129
130
|
# File 'lib/dommy/shadow_root.rb', line 126
def query_selector_all(selector)
return NodeList.new if selector.nil? || selector.to_s.empty?
NodeList.new(@__node__.css(selector.to_s).map { |n| @document.wrap_node(n) }.compact)
end
|
#replace_children(*args) ⇒ Object
111
112
113
114
115
116
117
118
|
# File 'lib/dommy/shadow_root.rb', line 111
def replace_children(*args)
removed = @__node__.children.to_a
removed.each(&:unlink)
nodes = args.flat_map { |a| detach_dom_nodes(a) }
nodes.each { |n| @__node__.add_child(n) }
@document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: removed)
nil
end
|
#text_content ⇒ Object
47
48
49
|
# File 'lib/dommy/shadow_root.rb', line 47
def text_content
@__node__.text
end
|
#text_content=(value) ⇒ Object
51
52
53
54
|
# File 'lib/dommy/shadow_root.rb', line 51
def text_content=(value)
@__node__.children.each(&:unlink)
@__node__.add_child(Backend.create_text(value.to_s, @document.nokogiri_doc))
end
|