Class: Dommy::Element
- Inherits:
-
Object
- Object
- Dommy::Element
- Includes:
- EventTarget, Node
- Defined in:
- lib/dommy/element.rb
Direct Known Subclasses
Constant Summary collapse
- SHADOW_HOST_TAGS =
Elements that may host a Shadow DOM tree per the HTML spec. Custom-element-style names (containing “-”) are also allowed.
%w[ article aside blockquote body div footer h1 h2 h3 h4 h5 h6 header main nav p section span ] .freeze
- ELEMENT_NODE =
Node type / NodeFilter bitmask constants — DOM Level 3 says these are exposed on both the constructor and every instance. Defined at the bottom of the class so subclasses inherit them too.
1- ATTRIBUTE_NODE =
2- TEXT_NODE =
3- CDATA_SECTION_NODE =
4- PROCESSING_INSTRUCTION_NODE =
7- COMMENT_NODE =
8- DOCUMENT_NODE =
9- DOCUMENT_TYPE_NODE =
10- DOCUMENT_FRAGMENT_NODE =
11- DOCUMENT_POSITION_DISCONNECTED =
0x01- DOCUMENT_POSITION_PRECEDING =
0x02- DOCUMENT_POSITION_FOLLOWING =
0x04- DOCUMENT_POSITION_CONTAINS =
0x08- DOCUMENT_POSITION_CONTAINED_BY =
0x10- DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC =
0x20- JS_METHOD_NAMES =
Methods routed through js_call (keep in sync with its when-arms).
%w[ getAttribute setAttribute hasAttribute removeAttribute getAttributeNames closest querySelector querySelectorAll getElementsByClassName getElementsByTagName insertAdjacentElement insertAdjacentHTML insertAdjacentText toggleAttribute matches toString getAttributeNode setAttributeNode removeAttributeNode focus blur attachShadow addEventListener removeEventListener dispatchEvent appendChild insertBefore removeChild replaceChild cloneNode append prepend replaceChildren before after getInnerHTML getHTML remove replaceWith click getBoundingClientRect getClientRects scrollIntoView scroll scrollTo scrollBy requestFullscreen showPopover hidePopover togglePopover ].freeze
Instance Attribute Summary collapse
-
#document ⇒ Object
readonly
Returns the value of attribute document.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #[]=(key, value) ⇒ Object
- #__dommy_backend_node__ ⇒ Object
-
#__internal_shadow_root__ ⇒ Object
Internal — gives access to the shadow root regardless of mode.
- #__js_call__(method, args) ⇒ Object
- #__js_get__(key) ⇒ Object
- #__js_method_names__ ⇒ Object
- #__js_set__(key, value) ⇒ Object
-
#__test_scroll_log__ ⇒ Object
Test inspector for scroll calls (no real layout to scroll).
- #after(*args) ⇒ Object
-
#anchor_href ⇒ Object
Anchor / area ‘href` IDL attribute reflects the attribute resolved against the document base URL (browser semantics).
-
#animate(keyframes, options = nil) ⇒ Object
Web Animations: start an animation on this element.
-
#append(*args) ⇒ Object
ParentNode mixin methods — append / prepend / replaceChildren take a mix of Node and String args (strings become text nodes).
- #append_child(child) ⇒ Object
-
#at_xpath(expression) ⇒ Object
XPath queries scoped to this element, returning wrapped nodes.
-
#attach_shadow(options = nil) ⇒ Object
‘el.attachShadow({ mode: “open” | “closed” })` — creates and attaches a ShadowRoot.
-
#attributes ⇒ Object
NamedNodeMap of attributes.
-
#base_uri ⇒ Object
‘Node.baseURI` — resolves against the document’s base URL, which in turn honors the first ‘<base href>` element (see `Document#base_uri`).
-
#before(*args) ⇒ Object
ChildNode mixin — before / after / replaceWith with mixed args.
- #blur ⇒ Object
- #child_element_count ⇒ Object
- #child_nodes ⇒ Object
- #children ⇒ Object
- #class_list ⇒ Object
- #class_name ⇒ Object
- #class_name=(value) ⇒ Object
- #click ⇒ Object
- #clone_node(deep_arg) ⇒ Object
- #closest(selector) ⇒ Object
-
#compare_document_position(other) ⇒ Object
Standard DOM compareDocumentPosition.
-
#contains?(other) ⇒ Boolean
‘el.contains(other)` — true if `other` is `el` itself or any descendant.
- #dataset ⇒ Object
-
#equal_node?(other) ⇒ Boolean
Structural equality — same nodeType, same tagName, same attribute set, and recursively-equal children.
- #first_child ⇒ Object
- #first_element_child ⇒ Object
-
#focus ⇒ Object
‘focus()` / `blur()` — Dommy has no layout / real focus, but tests rely on `document.activeElement` updating.
- #get_animations(_options = nil) ⇒ Object (also: #getAnimations)
- #get_attribute(name) ⇒ Object
- #get_attribute_node(name) ⇒ Object
- #get_elements_by_class_name(name) ⇒ Object
- #get_elements_by_tag_name(name) ⇒ Object
- #get_html(_options = nil) ⇒ Object
-
#get_inner_html(_options = nil) ⇒ Object
‘getInnerHTML()` — happy-dom alias for the `innerHTML` getter.
- #has_attribute?(name) ⇒ Boolean
- #has_attributes? ⇒ Boolean
- #has_child_nodes? ⇒ Boolean
- #id ⇒ Object
- #id=(value) ⇒ Object
-
#initialize(document, nokogiri_node) ⇒ Element
constructor
A new instance of Element.
- #inner_html ⇒ Object
- #inner_html=(value) ⇒ Object
-
#insert_adjacent_element(position, element) ⇒ Object
‘el.insertAdjacentElement(position, element)` — DOM spec positions: “beforebegin”, “afterbegin”, “beforeend”, “afterend”.
- #insert_adjacent_html(position, html) ⇒ Object
- #insert_adjacent_text(position, text) ⇒ Object
- #insert_before(child, reference) ⇒ Object
-
#is_connected? ⇒ Boolean
(also: #connected?)
Walks parents up to the Document (or false when the chain dead-ends).
- #last_child ⇒ Object
- #last_element_child ⇒ Object
-
#live_child_nodes ⇒ Object
Live NodeList over this element’s children.
- #local_name ⇒ Object
- #matches?(selector) ⇒ Boolean
-
#namespace_uri ⇒ Object
HTML namespace constants — most HTML elements live in xhtml ns.
- #next_element_sibling ⇒ Object
- #next_sibling ⇒ Object
-
#normalize ⇒ Object
Merge adjacent text node siblings and drop empty text nodes.
-
#on(type, &block) ⇒ Object
Ruby block-style listener (in addition to the (type, callable, options) form inherited from EventTarget).
-
#outer_html ⇒ Object
Outer HTML — serializes this element and its subtree.
-
#outer_html=(html) ⇒ Object
Per WHATWG DOM Parsing: - parent is null (detached element) → return silently - parent is the Document (‘<html>` element) → throw NoModificationAllowedError (can’t replace the document element via this API) - otherwise, parse ‘html` as a fragment in the parent’s context and replace this element with the parsed nodes.
- #owner_document ⇒ Object
- #parent_element ⇒ Object (also: #parent)
- #parent_node ⇒ Object
-
#path ⇒ Object
The XPath string locating this element in its document.
- #prepend(*args) ⇒ Object
- #previous_element_sibling ⇒ Object
- #previous_sibling ⇒ Object
- #query_selector(selector) ⇒ Object
- #query_selector_all(selector) ⇒ Object
-
#reflected_attr_name(key) ⇒ Object
Map a JS boolean property name to its underlying HTML attribute.
- #remove ⇒ Object
- #remove_attribute(name) ⇒ Object
- #remove_attribute_node(attr) ⇒ Object
- #remove_child(child) ⇒ Object
-
#replace_child(new_child, old_child) ⇒ Object
‘node.replaceChild(newChild, oldChild)` — required for in-place item updates in list reconcilers.
- #replace_children(*args) ⇒ Object
- #replace_with_nodes(*args) ⇒ Object
- #role ⇒ Object
- #role=(value) ⇒ Object
-
#root_node ⇒ Object
(also: #get_root_node)
‘el.getRootNode()` — returns the topmost ancestor (document, ShadowRoot, fragment, or self if detached).
-
#same_node?(other) ⇒ Boolean
‘Node.isSameNode(other)` — strict reference identity.
- #set_attribute(name, value) ⇒ Object
- #set_attribute_node(attr) ⇒ Object
-
#shadow_root ⇒ Object
‘el.shadowRoot` — returns the attached ShadowRoot only when mode is “open”; closed shadows are hidden from external code.
-
#slot ⇒ Object
‘slot` and `role` are simple reflected string attributes — added as named accessors for happy-dom test parity.
- #slot=(value) ⇒ Object
- #style ⇒ Object
- #tag_name ⇒ Object
-
#text_content ⇒ Object
—– Public Ruby API (snake_case) —–.
- #text_content=(value) ⇒ Object
-
#to_s ⇒ Object
Convenience alias matching the DOM idiom ‘String(el)` → outerHTML.
- #toggle_attribute(name, force = nil) ⇒ Object
- #xpath(expression) ⇒ Object
Methods included from EventTarget
#__internal_deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener
Constructor Details
#initialize(document, nokogiri_node) ⇒ Element
Returns a new instance of Element.
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 |
# File 'lib/dommy/element.rb', line 836 def initialize(document, nokogiri_node) @document = document @__node__ = nokogiri_node @class_list = ClassList.new(self) @style = StyleDeclaration.new(self) @dataset = DatasetMap.new(self) # `HTMLCollection` re-evaluates the child list on every # property access so callers that capture `el[:children]` once # see DOM mutations made between iterations — required by list # reconciliation patterns that rely on the spec's live # HTMLCollection semantics to detect already-positioned nodes. @live_children = HTMLCollection.new do @__node__.element_children.map { |n| @document.wrap_node(n) }.compact end end |
Instance Attribute Details
#document ⇒ Object (readonly)
Returns the value of attribute document.
832 833 834 |
# File 'lib/dommy/element.rb', line 832 def document @document end |
Instance Method Details
#[](key) ⇒ Object
1546 1547 1548 |
# File 'lib/dommy/element.rb', line 1546 def [](key) __js_get__(key.to_s) end |
#[]=(key, value) ⇒ Object
1550 1551 1552 |
# File 'lib/dommy/element.rb', line 1550 def []=(key, value) __js_set__(key.to_s, value) end |
#__dommy_backend_node__ ⇒ Object
834 |
# File 'lib/dommy/element.rb', line 834 def __dommy_backend_node__ = @__node__ |
#__internal_shadow_root__ ⇒ Object
Internal — gives access to the shadow root regardless of mode. Used by event composition / ‘composedPath()`.
1287 1288 1289 |
# File 'lib/dommy/element.rb', line 1287 def __internal_shadow_root__ @__shadow_root end |
#__js_call__(method, args) ⇒ Object
1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 |
# File 'lib/dommy/element.rb', line 1777 def __js_call__(method, args) case method when "getAttribute" get_attribute(args[0]) when "setAttribute" set_attribute(args[0], args[1]) when "hasAttribute" has_attribute?(args[0]) when "removeAttribute" remove_attribute(args[0]) when "getAttributeNames" @__node__.attribute_nodes.map(&:name) when "closest" closest(args[0]) when "querySelector" query_selector(args[0]) when "querySelectorAll" query_selector_all(args[0]) when "getElementsByClassName" get_elements_by_class_name(args[0]) when "getElementsByTagName" get_elements_by_tag_name(args[0]) when "insertAdjacentElement" insert_adjacent_element(args[0], args[1]) when "insertAdjacentHTML" insert_adjacent_html(args[0], args[1]) when "insertAdjacentText" insert_adjacent_text(args[0], args[1]) when "toggleAttribute" toggle_attribute(args[0], args[1]) when "matches" matches?(args[0]) when "toString" to_s when "getAttributeNode" get_attribute_node(args[0]) when "setAttributeNode" set_attribute_node(args[0]) when "removeAttributeNode" remove_attribute_node(args[0]) when "focus" focus when "blur" blur when "attachShadow" attach_shadow(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]) when "appendChild" append_child(args[0]) when "insertBefore" insert_before(args[0], args[1]) when "removeChild" remove_child(args[0]) when "replaceChild" replace_child(args[0], args[1]) when "cloneNode" clone_node(args[0]) when "append" append_nodes(args) when "prepend" prepend_nodes(args) when "replaceChildren" replace_children(*args) when "before" insert_adjacent(:before, args) when "after" insert_adjacent(:after, args) when "getInnerHTML", "getHTML" inner_html when "remove" parent = @__node__.parent @__node__.unlink @document.notify_child_list_mutation(target_node: parent, added_nodes: [], removed_nodes: [@__node__]) if parent nil when "replaceWith" replace_with(args) when "click" dispatch_event(MouseEvent.new("click", "bubbles" => true, "cancelable" => true, "button" => 0)) when "getBoundingClientRect" DOMRect.new when "getClientRects" [] when "scrollIntoView", "scroll", "scrollTo", "scrollBy" # No layout — record the request for tests to assert against. @scroll_log ||= [] @scroll_log << [method, args] nil when "requestFullscreen" @document.__internal_set_fullscreen_element__(self) PromiseValue.resolve(@document.default_view, nil) when "showPopover" toggle_popover_state(true) nil when "hidePopover" toggle_popover_state(false) nil when "togglePopover" new_state = !@__popover_open__ toggle_popover_state(new_state) new_state else nil end end |
#__js_get__(key) ⇒ Object
1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 |
# File 'lib/dommy/element.rb', line 1554 def __js_get__(key) case key when "nodeType" 1 when "isConnected" is_connected? when "scrollTop", "scrollLeft", "scrollWidth", "scrollHeight", "clientWidth", "clientHeight", "clientTop", "clientLeft", "offsetWidth", "offsetHeight", "offsetTop", "offsetLeft" # No layout engine — zeroed values match what real browsers # report for hidden / pre-paint elements. 0 when "offsetParent" nil when "popover" get_attribute("popover") when "children" @live_children when "firstElementChild" @document.wrap_node(@__node__.element_children.first) when "parentElement", "parent" wrap_parent(@__node__.parent) when "parentNode" # `parentNode` is broader than `parentElement` — includes # DocumentFragment / Document parents too. Reconcilers use # this to find the host before calling replaceChild. @__node__.parent && @document.wrap_node(@__node__.parent) when "textContent" @__node__.text when "innerHTML" if @__node__.name == "template" @document.template_content_inner_html(self) else @__node__.inner_html end when "tagName" @__node__.name.upcase when "classList" @class_list when "style" @style when "dataset" @dataset when "content" template_content when "className" # DOM reflects the `class` attribute as the `className` string # property (space-separated tokens, "" when absent). @__node__["class"].to_s when "id" @__node__["id"].to_s when "hidden", "disabled", "checked", "readOnly", "multiple", "required" # Boolean reflected properties — true iff the matching HTML # attribute is present. Real DOM normalizes attribute names to # lowercase, mapped here too (e.g. `readOnly` ↔ `readonly`). @__node__.key?(reflected_attr_name(key)) when "value" # For form elements `value` is a property that defaults to the # `value` attribute. We don't model the property/attribute # split here — both reads and writes go through the attribute. @__node__["value"].to_s when "href" anchor_href when "attributes" attributes when "namespaceURI" namespace_uri when "localName" local_name when "nodeName" @__node__.name.upcase when "slot" slot when "role" role when "baseURI" base_uri when "shadowRoot" shadow_root when "ownerDocument" @document else # `el.onXxx` event handler property — returns the registered # callback (if any), or nil. if key.start_with?("on") && key.length > 2 @on_handlers&.[](event_name_from_on(key)) end end end |
#__js_method_names__ ⇒ Object
1773 1774 1775 |
# File 'lib/dommy/element.rb', line 1773 def __js_method_names__ JS_METHOD_NAMES end |
#__js_set__(key, value) ⇒ Object
1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 |
# File 'lib/dommy/element.rb', line 1676 def __js_set__(key, value) case key when "textContent" # `node.content =` removes all existing children and (if # value is non-empty) appends a single text node. Capture # before/after to feed MutationObserver — mirrors the # innerHTML branch below. removed = @__node__.children.to_a @__node__.content = value.to_s added = @__node__.children.to_a if removed.any? || added.any? @document.notify_child_list_mutation( target_node: @__node__, added_nodes: added, removed_nodes: removed ) end when "innerHTML" removed = @__node__.children.to_a if @__node__.name == "template" # `<template>` content is invisible to outer selectors in # real DOM (it lives in a separate DocumentFragment exposed # via `[:content]`). Mirror that here so child placeholders # inside the template don't pollute outer queries. @document.attach_template_content(self, value.to_s) else @__node__.inner_html = value.to_s @document.migrate_template_descendants(@__node__) end @document.notify_child_list_mutation( target_node: @__node__, added_nodes: @__node__.children.to_a, removed_nodes: removed ) when "hidden", "disabled", "checked", "readOnly", "multiple", "required" # Boolean reflected property — funnel through set_attribute / # remove_attribute so MutationObserver attribute records fire. name = reflected_attr_name(key) if value set_attribute(name, "") elsif @__node__.key?(name) remove_attribute(name) end when "className" set_attribute("class", value.to_s) when "id" set_attribute("id", value.to_s) when "value" set_attribute("value", value.to_s) when "slot" set_attribute("slot", value.to_s) when "role" set_attribute("role", value.to_s) else # `el.onXxx = fn` registers fn as a single named handler. # Setting to nil removes it. Mirrors HTMLElement IDL. if key.start_with?("on") && key.length > 2 set_on_handler(event_name_from_on(key), value) else nil end end end |
#__test_scroll_log__ ⇒ Object
Test inspector for scroll calls (no real layout to scroll).
2247 2248 2249 |
# File 'lib/dommy/element.rb', line 2247 def __test_scroll_log__ @scroll_log ||= [] end |
#after(*args) ⇒ Object
1512 1513 1514 |
# File 'lib/dommy/element.rb', line 1512 def after(*args) insert_adjacent(:after, args) end |
#anchor_href ⇒ Object
Anchor / area ‘href` IDL attribute reflects the attribute resolved against the document base URL (browser semantics). Routers rely on this to compare origins and detect external links.
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 |
# File 'lib/dommy/element.rb', line 1658 def anchor_href raw = @__node__["href"] return "" if raw.nil? win = @document.default_view base = win&.location ? win.location.href : "" URI.join(base, raw.to_s).to_s rescue URI::InvalidURIError, ArgumentError raw.to_s end |
#animate(keyframes, options = nil) ⇒ Object
Web Animations: start an animation on this element. Returns the new Animation. Dommy doesn’t interpolate; the animation simply transitions through the ‘playState` lifecycle, finishing via `scheduler.advance_time(duration)` or an explicit `animation.finish`.
1989 1990 1991 1992 1993 1994 1995 1996 |
# File 'lib/dommy/element.rb', line 1989 def animate(keyframes, = nil) effect = KeyframeEffect.new(self, keyframes, ) animation = Animation.new(effect, nil, window: @document.default_view) @__animations ||= [] @__animations << animation animation.play animation end |
#append(*args) ⇒ Object
ParentNode mixin methods — append / prepend / replaceChildren take a mix of Node and String args (strings become text nodes).
1489 1490 1491 |
# File 'lib/dommy/element.rb', line 1489 def append(*args) append_nodes(args) end |
#append_child(child) ⇒ Object
2031 2032 2033 2034 2035 2036 2037 |
# File 'lib/dommy/element.rb', line 2031 def append_child(child) check_hierarchy!(child) nodes = detach_dom_nodes(child) append_dom_nodes(nodes) @document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: []) child end |
#at_xpath(expression) ⇒ Object
XPath queries scoped to this element, returning wrapped nodes.
2017 2018 2019 2020 |
# File 'lib/dommy/element.rb', line 2017 def at_xpath(expression) node = @__node__.at_xpath(expression) node && @document.wrap_node(node) end |
#attach_shadow(options = nil) ⇒ Object
‘el.attachShadow({ mode: “open” | “closed” })` — creates and attaches a ShadowRoot. The shadow tree lives in its own Nokogiri fragment and is invisible to the outer querySelector / children chain. Per spec:
- the `mode` field is REQUIRED in the init dict
- only certain host element types are valid (see SHADOW_HOST_TAGS)
- re-attaching to an element that already has a shadow throws
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 |
# File 'lib/dommy/element.rb', line 1252 def attach_shadow( = nil) tag = @__node__.name.downcase unless SHADOW_HOST_TAGS.include?(tag) || tag.include?("-") raise DOMException::NotSupportedError, "<#{tag}> cannot host a shadow root" end raise DOMException::InvalidStateError, "Shadow root already attached" if @__shadow_root opts = .is_a?(Hash) ? : {} mode_raw = opts.key?("mode") ? opts["mode"] : opts[:mode] raise TypeError, "attachShadow init dictionary requires 'mode'" if mode_raw.nil? mode = mode_raw.to_s raise DOMException::SyntaxError, "mode must be 'open' or 'closed'" unless %w[open closed].include?(mode) @__shadow_root = ShadowRoot.new( self, mode: mode, delegates_focus: opts["delegatesFocus"] || opts[:delegatesFocus] || false, slot_assignment: opts["slotAssignment"] || opts[:slotAssignment] || "named" ) @__shadow_root end |
#attributes ⇒ Object
NamedNodeMap of attributes. Lazily allocated and re-used so ‘el.attributes === el.attributes` and `attr.ownerElement === el`.
1121 1122 1123 |
# File 'lib/dommy/element.rb', line 1121 def attributes @attributes ||= NamedNodeMap.new(self) end |
#base_uri ⇒ Object
‘Node.baseURI` — resolves against the document’s base URL, which in turn honors the first ‘<base href>` element (see `Document#base_uri`).
1170 1171 1172 |
# File 'lib/dommy/element.rb', line 1170 def base_uri @document.base_uri end |
#before(*args) ⇒ Object
ChildNode mixin — before / after / replaceWith with mixed args.
1508 1509 1510 |
# File 'lib/dommy/element.rb', line 1508 def before(*args) insert_adjacent(:before, args) end |
#blur ⇒ Object
1216 1217 1218 1219 |
# File 'lib/dommy/element.rb', line 1216 def blur @document.__internal_set_active_element__(nil) nil end |
#child_element_count ⇒ Object
937 938 939 |
# File 'lib/dommy/element.rb', line 937 def child_element_count @__node__.element_children.size end |
#child_nodes ⇒ Object
941 942 943 |
# File 'lib/dommy/element.rb', line 941 def child_nodes NodeList.new(@__node__.children.map { |n| @document.wrap_node(n) }.compact) end |
#children ⇒ Object
907 908 909 |
# File 'lib/dommy/element.rb', line 907 def children @live_children end |
#class_list ⇒ Object
895 896 897 |
# File 'lib/dommy/element.rb', line 895 def class_list @class_list end |
#class_name ⇒ Object
887 888 889 |
# File 'lib/dommy/element.rb', line 887 def class_name @__node__["class"].to_s end |
#class_name=(value) ⇒ Object
891 892 893 |
# File 'lib/dommy/element.rb', line 891 def class_name=(value) set_attribute("class", value.to_s) end |
#click ⇒ Object
1531 1532 1533 |
# File 'lib/dommy/element.rb', line 1531 def click __js_call__("click", []) end |
#clone_node(deep_arg) ⇒ Object
2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 |
# File 'lib/dommy/element.rb', line 2091 def clone_node(deep_arg) deep = !!deep_arg if deep @document.wrap_node( Parser.fragment(@__node__.to_html, owner_doc: @document.nokogiri_doc).children.find(&:element?) ) else clone = @document.create_element(@__node__.name) @__node__.attribute_nodes.each do |attr| clone.__js_call__("setAttribute", [attr.name, attr.value]) end clone end end |
#closest(selector) ⇒ Object
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 |
# File 'lib/dommy/element.rb', line 1971 def closest(selector) return nil if selector.nil? || selector.to_s.empty? node = @__node__ while node&.element? return @document.wrap_node(node) if matches_selector?(node, selector.to_s) node = node.parent end nil end |
#compare_document_position(other) ⇒ Object
Standard DOM compareDocumentPosition. Returns 0 for self, a CONTAINS/CONTAINED_BY bitmask for ancestor/descendant pairs, or PRECEDING/FOLLOWING for siblings (and DISCONNECTED for unrelated nodes).
1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 |
# File 'lib/dommy/element.rb', line 1392 def compare_document_position(other) return 0 if equal?(other) return DOCUMENT_POSITION_DISCONNECTED unless other.respond_to?(:__dommy_backend_node__) self_node = @__node__ other_node = other.__dommy_backend_node__ self_ancestors = ancestor_chain(self_node) other_ancestors = ancestor_chain(other_node) common = nil self_ancestors.each do |a| if other_ancestors.include?(a) common = a break end end unless common return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING end if common == self_node return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING elsif common == other_node return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING end # Sibling-of-some-level case: compare the two branch points # under the common ancestor. self_branch = branch_under(common, self_ancestors) other_branch = branch_under(common, other_ancestors) common.children.each do |child| if child == self_branch return DOCUMENT_POSITION_FOLLOWING elsif child == other_branch return DOCUMENT_POSITION_PRECEDING end end DOCUMENT_POSITION_DISCONNECTED end |
#contains?(other) ⇒ Boolean
‘el.contains(other)` — true if `other` is `el` itself or any descendant. Per spec, returns false for null/non-Node.
1021 1022 1023 1024 1025 1026 1027 1028 |
# File 'lib/dommy/element.rb', line 1021 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 |
#dataset ⇒ Object
903 904 905 |
# File 'lib/dommy/element.rb', line 903 def dataset @dataset end |
#equal_node?(other) ⇒ Boolean
Structural equality — same nodeType, same tagName, same attribute set, and recursively-equal children. Used by linkedom test suite and standard DOM Node.isEqualNode.
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 |
# File 'lib/dommy/element.rb', line 1445 def equal_node?(other) return false unless other.is_a?(Element) return false unless @__node__.name == other.__dommy_backend_node__.name return false unless attribute_signature == other.send(:attribute_signature) return false unless @__node__.children.size == other.__dommy_backend_node__.children.size @__node__.children.zip(other.__dommy_backend_node__.children).all? do |a, b| wa = @document.wrap_node(a) wb = @document.wrap_node(b) wa.respond_to?(:equal_node?) ? wa.equal_node?(wb) : a.content == b.content end end |
#first_child ⇒ Object
929 930 931 |
# File 'lib/dommy/element.rb', line 929 def first_child @document.wrap_node(@__node__.children.first) end |
#first_element_child ⇒ Object
921 922 923 |
# File 'lib/dommy/element.rb', line 921 def first_element_child @document.wrap_node(@__node__.element_children.first) end |
#focus ⇒ Object
‘focus()` / `blur()` — Dommy has no layout / real focus, but tests rely on `document.activeElement` updating. Track the most recently focused element on the document.
1211 1212 1213 1214 |
# File 'lib/dommy/element.rb', line 1211 def focus @document.__internal_set_active_element__(self) nil end |
#get_animations(_options = nil) ⇒ Object Also known as: getAnimations
1998 1999 2000 |
# File 'lib/dommy/element.rb', line 1998 def get_animations( = nil) (@__animations ||= []).dup end |
#get_attribute(name) ⇒ Object
1937 1938 1939 1940 1941 |
# File 'lib/dommy/element.rb', line 1937 def get_attribute(name) return nil if name.nil? @__node__[normalize_attr_key(name)] end |
#get_attribute_node(name) ⇒ Object
1125 1126 1127 |
# File 'lib/dommy/element.rb', line 1125 def get_attribute_node(name) attributes.get_named_item(name) end |
#get_elements_by_class_name(name) ⇒ Object
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 |
# File 'lib/dommy/element.rb', line 1096 def get_elements_by_class_name(name) tokens = name.to_s.split(/\s+/).reject(&:empty?) root = @__node__ doc = @document HTMLCollection.new do next [] if tokens.empty? selector = tokens.map { |t| ".#{t}" }.join("") root.css(selector).map { |n| doc.wrap_node(n) }.compact end end |
#get_elements_by_tag_name(name) ⇒ Object
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 |
# File 'lib/dommy/element.rb', line 1108 def get_elements_by_tag_name(name) n = name.to_s.downcase root = @__node__ doc = @document if n == "*" HTMLCollection.new { root.css("*").map { |x| doc.wrap_node(x) }.compact } else HTMLCollection.new { root.css(n).map { |x| doc.wrap_node(x) }.compact } end end |
#get_html(_options = nil) ⇒ Object
1527 1528 1529 |
# File 'lib/dommy/element.rb', line 1527 def get_html( = nil) inner_html end |
#get_inner_html(_options = nil) ⇒ Object
‘getInnerHTML()` — happy-dom alias for the `innerHTML` getter. Real browsers add a `{ includeShadowRoots }` option which we ignore (no Shadow DOM in Dommy).
1523 1524 1525 |
# File 'lib/dommy/element.rb', line 1523 def get_inner_html( = nil) inner_html end |
#has_attribute?(name) ⇒ Boolean
1953 1954 1955 1956 1957 |
# File 'lib/dommy/element.rb', line 1953 def has_attribute?(name) return false if name.nil? @__node__.key?(normalize_attr_key(name)) end |
#has_attributes? ⇒ Boolean
957 958 959 |
# File 'lib/dommy/element.rb', line 957 def has_attributes? @__node__.attribute_nodes.any? end |
#has_child_nodes? ⇒ Boolean
953 954 955 |
# File 'lib/dommy/element.rb', line 953 def has_child_nodes? @__node__.children.any? end |
#id ⇒ Object
879 880 881 |
# File 'lib/dommy/element.rb', line 879 def id @__node__["id"].to_s end |
#id=(value) ⇒ Object
883 884 885 |
# File 'lib/dommy/element.rb', line 883 def id=(value) set_attribute("id", value.to_s) end |
#inner_html ⇒ Object
867 868 869 |
# File 'lib/dommy/element.rb', line 867 def inner_html __js_get__("innerHTML") end |
#inner_html=(value) ⇒ Object
871 872 873 |
# File 'lib/dommy/element.rb', line 871 def inner_html=(value) __js_set__("innerHTML", value) end |
#insert_adjacent_element(position, element) ⇒ Object
‘el.insertAdjacentElement(position, element)` — DOM spec positions: “beforebegin”, “afterbegin”, “beforeend”, “afterend”. Returns the inserted element or nil if position has no anchor (root cases).
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 |
# File 'lib/dommy/element.rb', line 1294 def insert_adjacent_element(position, element) return nil unless element.respond_to?(:__dommy_backend_node__) case position.to_s when "beforebegin" return nil unless @__node__.parent node = detach_for_insert(element) @__node__.add_previous_sibling(node) @document.notify_child_list_mutation(target_node: @__node__.parent, added_nodes: [node], removed_nodes: []) when "afterbegin" node = detach_for_insert(element) first = @__node__.children.first first ? first.add_previous_sibling(node) : @__node__.add_child(node) @document.notify_child_list_mutation(target_node: @__node__, added_nodes: [node], removed_nodes: []) when "beforeend" node = detach_for_insert(element) @__node__.add_child(node) @document.notify_child_list_mutation(target_node: @__node__, added_nodes: [node], removed_nodes: []) when "afterend" return nil unless @__node__.parent node = detach_for_insert(element) @__node__.add_next_sibling(node) @document.notify_child_list_mutation(target_node: @__node__.parent, added_nodes: [node], removed_nodes: []) else return nil end element end |
#insert_adjacent_html(position, html) ⇒ Object
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 |
# File 'lib/dommy/element.rb', line 1326 def insert_adjacent_html(position, html) fragment = Parser.fragment(html.to_s, owner_doc: @__node__.document) nodes = fragment.children.to_a case position.to_s when "beforebegin" return nil unless @__node__.parent nodes.reverse_each { |n| @__node__.add_previous_sibling(n) } @document.notify_child_list_mutation(target_node: @__node__.parent, added_nodes: nodes, removed_nodes: []) when "afterbegin" first = @__node__.children.first if first nodes.reverse_each { |n| first.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: []) when "beforeend" nodes.each { |n| @__node__.add_child(n) } @document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: []) when "afterend" return nil unless @__node__.parent nodes.reverse_each { |n| @__node__.add_next_sibling(n) } @document.notify_child_list_mutation(target_node: @__node__.parent, added_nodes: nodes, removed_nodes: []) end nil end |
#insert_adjacent_text(position, text) ⇒ Object
1357 1358 1359 1360 1361 |
# File 'lib/dommy/element.rb', line 1357 def insert_adjacent_text(position, text) return nil if text.to_s.empty? insert_adjacent_element(position, @document.create_text_node(text.to_s)) end |
#insert_before(child, reference) ⇒ Object
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 |
# File 'lib/dommy/element.rb', line 2039 def insert_before(child, reference) check_hierarchy!(child) nodes = detach_dom_nodes(child) if reference.nil? append_dom_nodes(nodes) else ref_node = unwrap_dom_node(reference) if ref_node&.parent != @__node__ # Per spec this should be a NotFoundError, but the legacy # behaviour of `appendChild` when reference is foreign is a # silent append. Preserve that for compatibility. append_dom_nodes(nodes) else nodes.reverse_each { |node| ref_node.add_previous_sibling(node) } end end @document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: []) child end |
#is_connected? ⇒ Boolean Also known as: connected?
Walks parents up to the Document (or false when the chain dead-ends). Crosses ShadowRoot boundaries: a node inside an open or closed shadow tree is connected iff its host is.
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 |
# File 'lib/dommy/element.rb', line 1181 def is_connected? current = @__node__ seen = {} loop do # Guard against unexpected cycles in malformed trees. return false if seen[current.object_id] seen[current.object_id] = true parent = current.respond_to?(:parent) ? current.parent : nil return false unless parent return true if parent.is_a?(Backend.document_class) sr = @document.__internal_shadow_root_for_fragment__(parent) if sr host = sr.host return false unless host current = host.__dommy_backend_node__ else current = parent end end end |
#last_child ⇒ Object
933 934 935 |
# File 'lib/dommy/element.rb', line 933 def last_child @document.wrap_node(@__node__.children.last) end |
#last_element_child ⇒ Object
925 926 927 |
# File 'lib/dommy/element.rb', line 925 def last_element_child @document.wrap_node(@__node__.element_children.last) end |
#live_child_nodes ⇒ Object
Live NodeList over this element’s children. Reflects later mutations on every access.
947 948 949 950 951 |
# File 'lib/dommy/element.rb', line 947 def live_child_nodes @live_child_nodes ||= LiveNodeList.new do @__node__.children.map { |n| @document.wrap_node(n) }.compact end end |
#local_name ⇒ Object
1145 1146 1147 |
# File 'lib/dommy/element.rb', line 1145 def local_name @__node__.name.downcase end |
#matches?(selector) ⇒ Boolean
1088 1089 1090 1091 1092 1093 1094 |
# File 'lib/dommy/element.rb', line 1088 def matches?(selector) return false if selector.nil? || selector.to_s.empty? # `:scope` pseudo — match against this element itself. sel = selector.to_s.gsub(":scope", "*:nth-last-child(n)") matches_selector?(@__node__, sel) end |
#namespace_uri ⇒ Object
HTML namespace constants — most HTML elements live in xhtml ns.
1140 1141 1142 1143 |
# File 'lib/dommy/element.rb', line 1140 def namespace_uri ns = @__node__.namespace ns ? ns.href : "http://www.w3.org/1999/xhtml" end |
#next_element_sibling ⇒ Object
969 970 971 972 973 |
# File 'lib/dommy/element.rb', line 969 def next_element_sibling node = @__node__.next node = node.next while node && !node.element? node && @document.wrap_node(node) end |
#next_sibling ⇒ Object
961 962 963 |
# File 'lib/dommy/element.rb', line 961 def next_sibling @__node__.next && @document.wrap_node(@__node__.next) end |
#normalize ⇒ Object
Merge adjacent text node siblings and drop empty text nodes.
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 |
# File 'lib/dommy/element.rb', line 1059 def normalize @__node__.traverse do |node| next unless node.text? next if node.parent.nil? if node.content == "" && node.parent node.unlink elsif node.next && node.next.text? node.content = node.content + node.next.content node.next.unlink end end nil end |
#on(type, &block) ⇒ Object
Ruby block-style listener (in addition to the (type, callable, options) form inherited from EventTarget). Returns the resolved listener so callers can pass it back to remove_event_listener.
1538 1539 1540 1541 |
# File 'lib/dommy/element.rb', line 1538 def on(type, &block) add_event_listener(type, block) block end |
#outer_html ⇒ Object
Outer HTML — serializes this element and its subtree. Setter replaces this element in its parent with the parsed fragment.
983 984 985 |
# File 'lib/dommy/element.rb', line 983 def outer_html @__node__.to_html end |
#outer_html=(html) ⇒ Object
Per WHATWG DOM Parsing:
- parent is null (detached element) → return silently
- parent is the Document (`<html>` element) → throw
NoModificationAllowedError (can't replace the document
element via this API)
- otherwise, parse `html` as a fragment in the parent's
context and replace this element with the parsed nodes
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 |
# File 'lib/dommy/element.rb', line 994 def outer_html=(html) parent = @__node__.parent return unless parent if parent.is_a?(Backend.document_class) raise( DOMException::NoModificationAllowedError, "outerHTML setter not allowed on the document element" ) end fragment = Parser.fragment(html.to_s, owner_doc: @__node__.document) anchor = @__node__.next_sibling removed = @__node__ new_nodes = fragment.children.to_a @__node__.unlink if anchor new_nodes.reverse_each { |n| anchor.add_previous_sibling(n) } else new_nodes.each { |n| parent.add_child(n) } end @document.notify_child_list_mutation(target_node: parent, added_nodes: new_nodes, removed_nodes: [removed]) end |
#owner_document ⇒ Object
1174 1175 1176 |
# File 'lib/dommy/element.rb', line 1174 def owner_document @document end |
#parent_element ⇒ Object Also known as: parent
911 912 913 |
# File 'lib/dommy/element.rb', line 911 def parent_element @document.wrap_node(@__node__.parent) if @__node__.parent&.element? end |
#parent_node ⇒ Object
917 918 919 |
# File 'lib/dommy/element.rb', line 917 def parent_node @__node__.parent && @document.wrap_node(@__node__.parent) end |
#path ⇒ Object
The XPath string locating this element in its document.
2027 2028 2029 |
# File 'lib/dommy/element.rb', line 2027 def path @__node__.path end |
#prepend(*args) ⇒ Object
1493 1494 1495 |
# File 'lib/dommy/element.rb', line 1493 def prepend(*args) prepend_nodes(args) end |
#previous_element_sibling ⇒ Object
975 976 977 978 979 |
# File 'lib/dommy/element.rb', line 975 def previous_element_sibling node = @__node__.previous node = node.previous while node && !node.element? node && @document.wrap_node(node) end |
#previous_sibling ⇒ Object
965 966 967 |
# File 'lib/dommy/element.rb', line 965 def previous_sibling @__node__.previous && @document.wrap_node(@__node__.previous) end |
#query_selector(selector) ⇒ Object
2004 2005 2006 2007 2008 |
# File 'lib/dommy/element.rb', line 2004 def query_selector(selector) return nil if selector.nil? || selector.to_s.empty? @document.wrap_node(@__node__.at_css(selector.to_s, Internal::CSS_PSEUDO_HANDLERS)) end |
#query_selector_all(selector) ⇒ Object
2010 2011 2012 2013 2014 |
# File 'lib/dommy/element.rb', line 2010 def query_selector_all(selector) return NodeList.new if selector.nil? || selector.to_s.empty? NodeList.new(@__node__.css(selector.to_s, Internal::CSS_PSEUDO_HANDLERS).map { |node| @document.wrap_node(node) }.compact) end |
#reflected_attr_name(key) ⇒ Object
Map a JS boolean property name to its underlying HTML attribute. HTML attribute names are lowercase; the DOM property may be camelCase (‘readOnly` → `readonly`).
1672 1673 1674 |
# File 'lib/dommy/element.rb', line 1672 def reflected_attr_name(key) {"readOnly" => "readonly"}.fetch(key, key) end |
#remove ⇒ Object
1482 1483 1484 |
# File 'lib/dommy/element.rb', line 1482 def remove __js_call__("remove", []) end |
#remove_attribute(name) ⇒ Object
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 |
# File 'lib/dommy/element.rb', line 1959 def remove_attribute(name) return nil if name.nil? key = normalize_attr_key(name) return nil unless @__node__.key?(key) old = @__node__[key] @__node__.remove_attribute(key) @document.notify_attribute_mutation(target_node: @__node__, attribute_name: key, old_value: old) nil end |
#remove_attribute_node(attr) ⇒ Object
1133 1134 1135 1136 1137 |
# File 'lib/dommy/element.rb', line 1133 def remove_attribute_node(attr) return nil unless attr.respond_to?(:name) attributes.remove_named_item(attr.name) end |
#remove_child(child) ⇒ Object
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 |
# File 'lib/dommy/element.rb', line 2060 def remove_child(child) node = unwrap_dom_node(child) unless node&.parent == @__node__ raise DOMException::NotFoundError, "node is not a child of this element" end node.unlink @document.notify_child_list_mutation(target_node: @__node__, added_nodes: [], removed_nodes: [node]) child end |
#replace_child(new_child, old_child) ⇒ Object
‘node.replaceChild(newChild, oldChild)` — required for in-place item updates in list reconcilers. Inserts newChild where oldChild was, then unlinks oldChild. Notifies MutationObserver of both changes in one record so observers see the swap atomically.
2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 |
# File 'lib/dommy/element.rb', line 2076 def replace_child(new_child, old_child) old_node = unwrap_dom_node(old_child) return nil unless old_node&.parent == @__node__ new_nodes = detach_dom_nodes(new_child) new_nodes.reverse_each { |node| old_node.add_previous_sibling(node) } old_node.unlink @document.notify_child_list_mutation( target_node: @__node__, added_nodes: new_nodes, removed_nodes: [old_node] ) old_child end |
#replace_children(*args) ⇒ Object
1497 1498 1499 1500 1501 1502 1503 1504 |
# File 'lib/dommy/element.rb', line 1497 def replace_children(*args) removed = @__node__.children.to_a removed.each(&:unlink) nodes = args.flat_map { |arg| detach_dom_nodes(arg) } nodes.each { |n| @__node__.add_child(n) } @document.notify_child_list_mutation(target_node: @__node__, added_nodes: nodes, removed_nodes: removed) nil end |
#replace_with_nodes(*args) ⇒ Object
1516 1517 1518 |
# File 'lib/dommy/element.rb', line 1516 def replace_with_nodes(*args) replace_with(args) end |
#role ⇒ Object
1159 1160 1161 |
# File 'lib/dommy/element.rb', line 1159 def role @__node__["role"].to_s end |
#role=(value) ⇒ Object
1163 1164 1165 |
# File 'lib/dommy/element.rb', line 1163 def role=(value) set_attribute("role", value.to_s) end |
#root_node ⇒ Object Also known as: get_root_node
‘el.getRootNode()` — returns the topmost ancestor (document, ShadowRoot, fragment, or self if detached). If the element lives inside a shadow tree, returns that ShadowRoot. Otherwise walks until we hit the Nokogiri Document (then returns the Document).
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
# File 'lib/dommy/element.rb', line 1034 def root_node sr = @document.__internal_shadow_root_containing__(@__node__) return sr if sr current = @__node__ attached = false loop do parent = current.respond_to?(:parent) ? current.parent : nil break unless parent if parent.is_a?(Backend.document_class) attached = true break end current = parent end return @document if attached @document.wrap_node(current) || @document end |
#same_node?(other) ⇒ Boolean
‘Node.isSameNode(other)` — strict reference identity. The DOM spec deprecates this in favor of `===`, but linkedom-style tests still call it.
1438 1439 1440 |
# File 'lib/dommy/element.rb', line 1438 def same_node?(other) equal?(other) end |
#set_attribute(name, value) ⇒ Object
1943 1944 1945 1946 1947 1948 1949 1950 1951 |
# File 'lib/dommy/element.rb', line 1943 def set_attribute(name, value) return nil if name.nil? key = normalize_attr_key(name) old = @__node__[key] @__node__[key] = value.to_s @document.notify_attribute_mutation(target_node: @__node__, attribute_name: key, old_value: old) nil end |
#set_attribute_node(attr) ⇒ Object
1129 1130 1131 |
# File 'lib/dommy/element.rb', line 1129 def set_attribute_node(attr) attributes.set_named_item(attr) end |
#shadow_root ⇒ Object
‘el.shadowRoot` — returns the attached ShadowRoot only when mode is “open”; closed shadows are hidden from external code.
1278 1279 1280 1281 1282 1283 |
# File 'lib/dommy/element.rb', line 1278 def shadow_root return nil unless @__shadow_root return nil if @__shadow_root.mode == "closed" @__shadow_root end |
#slot ⇒ Object
‘slot` and `role` are simple reflected string attributes —added as named accessors for happy-dom test parity.
1151 1152 1153 |
# File 'lib/dommy/element.rb', line 1151 def slot @__node__["slot"].to_s end |
#slot=(value) ⇒ Object
1155 1156 1157 |
# File 'lib/dommy/element.rb', line 1155 def slot=(value) set_attribute("slot", value.to_s) end |
#style ⇒ Object
899 900 901 |
# File 'lib/dommy/element.rb', line 899 def style @style end |
#tag_name ⇒ Object
875 876 877 |
# File 'lib/dommy/element.rb', line 875 def tag_name @__node__.name.upcase end |
#text_content ⇒ Object
—– Public Ruby API (snake_case) —–
Mirrors HTMLElement DOM properties / methods in idiomatic Ruby form. The bridge protocol (‘js_get` / `js_call`) routes camelCase JS names through these same accessors, so any fix here is visible in both views.
859 860 861 |
# File 'lib/dommy/element.rb', line 859 def text_content @__node__.text end |
#text_content=(value) ⇒ Object
863 864 865 |
# File 'lib/dommy/element.rb', line 863 def text_content=(value) __js_set__("textContent", value) end |
#to_s ⇒ Object
Convenience alias matching the DOM idiom ‘String(el)` → outerHTML.
1364 1365 1366 |
# File 'lib/dommy/element.rb', line 1364 def to_s outer_html end |
#toggle_attribute(name, force = nil) ⇒ Object
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 |
# File 'lib/dommy/element.rb', line 1075 def toggle_attribute(name, force = nil) key = name.to_s.downcase present = @__node__.key?(key) desired = force.nil? ? !present : !!force if desired set_attribute(key, "") unless present true else remove_attribute(key) if present false end end |
#xpath(expression) ⇒ Object
2022 2023 2024 |
# File 'lib/dommy/element.rb', line 2022 def xpath(expression) @__node__.xpath(expression).map { |node| @document.wrap_node(node) } end |