Class: Obxcura::Node
- Inherits:
-
Object
- Object
- Obxcura::Node
- Defined in:
- lib/obxcura/node.rb
Overview
A live handle to a DOM node inside a Frame. Backed by a CDP remote object, so reads run as function calls bound to that object and reflect the current DOM. Returned by Frame::DOM#at_css / #css.
Instance Attribute Summary collapse
-
#remote_object_id ⇒ String
readonly
remote_object_id(notobject_id) so it doesn't shadow Ruby'sObject#object_id, which every object relies on for identity.
Instance Method Summary collapse
-
#[](name) ⇒ String?
(also: #attribute)
Read an attribute value.
-
#at_css(selector) ⇒ Obxcura::Node?
First descendant matching a CSS selector, searched within this node.
-
#focus ⇒ self
Give this node keyboard focus.
-
#initialize(frame, object_id) ⇒ Node
constructor
A new instance of Node.
-
#outer_html ⇒ String
The node's serialized outer HTML.
-
#submit ⇒ self
Submit this node's form.
-
#text ⇒ String
The visible text of the node and its descendants.
-
#type(*keys) ⇒ self
Type text into this node with real key events, one character at a time — Obscura implements
Input.dispatchKeyEvent, so the browser itself performs the insertion and fireskeydown,inputandkeyupthe way a keyboard would. -
#value ⇒ String?
The node's
value(form controls), or nil.
Constructor Details
#initialize(frame, object_id) ⇒ Node
Returns a new instance of Node.
15 16 17 18 |
# File 'lib/obxcura/node.rb', line 15 def initialize(frame, object_id) @frame = frame @remote_object_id = object_id end |
Instance Attribute Details
#remote_object_id ⇒ String (readonly)
remote_object_id (not object_id) so it doesn't shadow Ruby's
Object#object_id, which every object relies on for identity.
11 12 13 |
# File 'lib/obxcura/node.rb', line 11 def remote_object_id @remote_object_id end |
Instance Method Details
#[](name) ⇒ String? Also known as: attribute
Read an attribute value.
42 43 44 |
# File 'lib/obxcura/node.rb', line 42 def [](name) @frame.call_on(remote_object_id, "function(name) { return this.getAttribute(name); }", [ name ]) end |
#at_css(selector) ⇒ Obxcura::Node?
First descendant matching a CSS selector, searched within this node.
34 35 36 |
# File 'lib/obxcura/node.rb', line 34 def at_css(selector) @frame.to_node(@frame.call_on(remote_object_id, "function(s) { return this.querySelector(s); }", [ selector ])) end |
#focus ⇒ self
Give this node keyboard focus.
50 51 52 |
# File 'lib/obxcura/node.rb', line 50 def focus tap { @frame.page.command("DOM.focus", objectId: remote_object_id) } end |
#outer_html ⇒ String
Returns the node's serialized outer HTML.
107 108 109 |
# File 'lib/obxcura/node.rb', line 107 def outer_html @frame.call_on(remote_object_id, "function() { return this.outerHTML; }") end |
#submit ⇒ self
Submit this node's form. Works whether the node is the <form> itself or a
control inside one (resolved via .form / closest <form>).
Always goes through requestSubmit, which follows the interactive path:
constraint validation runs and a cancelable submit event fires. There is no
fallback to submit() — as of Obscura 0.1.11 the two genuinely differ, with
submit() bypassing the submit event entirely, so falling back would quietly
skip both validation and any listener a caller registered.
93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/obxcura/node.rb', line 93 def submit result = @frame.call_on(remote_object_id, <<~JS) function() { const form = this.tagName === "FORM" ? this : (this.form || this.closest("form")); if (!form) return { error: "node is not a form and has no ancestor form" }; form.requestSubmit(); } JS raise ProtocolError, result["error"] if result.is_a?(Hash) && result["error"] self end |
#text ⇒ String
Returns the visible text of the node and its descendants.
21 22 23 |
# File 'lib/obxcura/node.rb', line 21 def text @frame.call_on(remote_object_id, "function() { return this.textContent; }") end |
#type(*keys) ⇒ self
Type text into this node with real key events, one character at a time —
Obscura implements Input.dispatchKeyEvent, so the browser itself performs
the insertion and fires keydown, input and keyup the way a keyboard
would. Focuses the node first, since key events go to the active element.
Appends, matching keyboard behaviour when the node already has text.
Note change does not fire per keystroke — real browsers only fire it on
blur. The previous implementation synthesized both by assigning value
directly; listeners that relied on that change need to react to input.
Input.insertText is still unimplemented in Obscura 0.1.11, so there is no
bulk-insert fast path: this costs two CDP round trips per character. Fine for
form fields (100 chars ≈ 40ms), but it is genuinely slower than the old
single-assignment approach — 500 chars ≈ 0.11s — so don't use it to stuff
large text through. Assign value via Frame::Runtime#call_on for that, and
accept that no key events fire.
73 74 75 76 77 78 79 80 |
# File 'lib/obxcura/node.rb', line 73 def type(*keys) focus keys.join.each_char do |char| dispatch_key("keyDown", char, text: char) dispatch_key("keyUp", char) end self end |
#value ⇒ String?
Returns the node's value (form controls), or nil.
26 27 28 |
# File 'lib/obxcura/node.rb', line 26 def value @frame.call_on(remote_object_id, "function() { return this.value; }") end |