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
#__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.
19
20
21
22
23
24
25
26
27
|
# File 'lib/dommy/shadow_root.rb', line 19
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.__register_shadow_fragment__(@__node__, self)
end
|
Instance Attribute Details
#__node__ ⇒ Object
Returns the value of attribute __node__.
17
18
19
|
# File 'lib/dommy/shadow_root.rb', line 17
def __node__
@__node__
end
|
#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.
152
153
154
|
# File 'lib/dommy/shadow_root.rb', line 152
def [](key)
__js_get__(key.to_s)
end
|
#[]=(k, v) ⇒ Object
156
157
158
|
# File 'lib/dommy/shadow_root.rb', line 156
def []=(k, v)
__js_set__(k.to_s, v)
end
|
#__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).
236
237
238
|
# File 'lib/dommy/shadow_root.rb', line 236
def __event_parent__
nil
end
|
#__js_call__(method, args) ⇒ Object
204
205
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
|
# File 'lib/dommy/shadow_root.rb', line 204
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
160
161
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
|
# File 'lib/dommy/shadow_root.rb', line 160
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
193
194
195
196
197
198
199
200
201
202
|
# File 'lib/dommy/shadow_root.rb', line 193
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
89
90
91
92
93
94
|
# File 'lib/dommy/shadow_root.rb', line 89
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
82
83
84
85
86
87
|
# File 'lib/dommy/shadow_root.rb', line 82
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
62
63
64
|
# File 'lib/dommy/shadow_root.rb', line 62
def child_element_count
@__node__.element_children.size
end
|
#child_nodes ⇒ Object
58
59
60
|
# File 'lib/dommy/shadow_root.rb', line 58
def child_nodes
@__node__.children.map { |n| @document.wrap_node(n) }.compact
end
|
#children ⇒ Object
54
55
56
|
# File 'lib/dommy/shadow_root.rb', line 54
def children
@__node__.element_children.map { |n| @document.wrap_node(n) }.compact
end
|
#contains?(other) ⇒ Boolean
142
143
144
145
146
147
148
149
|
# File 'lib/dommy/shadow_root.rb', line 142
def contains?(other)
return false unless other.respond_to?(:__node__)
other_node = other.__node__
return true if other_node == @__node__
Internal::NodeTraversal.ancestor_of?(@__node__, other_node)
end
|
#first_child ⇒ Object
66
67
68
|
# File 'lib/dommy/shadow_root.rb', line 66
def first_child
@document.wrap_node(@__node__.children.first)
end
|
#first_element_child ⇒ Object
74
75
76
|
# File 'lib/dommy/shadow_root.rb', line 74
def first_element_child
@document.wrap_node(@__node__.element_children.first)
end
|
#get_element_by_id(id) ⇒ Object
130
131
132
133
134
|
# File 'lib/dommy/shadow_root.rb', line 130
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).
138
139
140
|
# File 'lib/dommy/shadow_root.rb', line 138
def get_root_node(_options = nil)
self
end
|
#inner_html ⇒ Object
—- Public Ruby API (ParentNode + DocumentFragment mixin) —-
31
32
33
|
# File 'lib/dommy/shadow_root.rb', line 31
def inner_html
@__node__.children.map(&:to_html).join
end
|
#inner_html=(html) ⇒ Object
35
36
37
38
39
40
41
42
43
|
# File 'lib/dommy/shadow_root.rb', line 35
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
70
71
72
|
# File 'lib/dommy/shadow_root.rb', line 70
def last_child
@document.wrap_node(@__node__.children.last)
end
|
#last_element_child ⇒ Object
78
79
80
|
# File 'lib/dommy/shadow_root.rb', line 78
def last_element_child
@document.wrap_node(@__node__.element_children.last)
end
|
#prepend(*args) ⇒ Object
96
97
98
99
100
101
102
103
104
105
106
107
|
# File 'lib/dommy/shadow_root.rb', line 96
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
118
119
120
121
122
|
# File 'lib/dommy/shadow_root.rb', line 118
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
124
125
126
127
128
|
# File 'lib/dommy/shadow_root.rb', line 124
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
109
110
111
112
113
114
115
116
|
# File 'lib/dommy/shadow_root.rb', line 109
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
45
46
47
|
# File 'lib/dommy/shadow_root.rb', line 45
def text_content
@__node__.text
end
|
#text_content=(value) ⇒ Object
49
50
51
52
|
# File 'lib/dommy/shadow_root.rb', line 49
def text_content=(value)
@__node__.children.each(&:unlink)
@__node__.add_child(Nokogiri::XML::Text.new(value.to_s, @document.nokogiri_doc))
end
|