Class: Dommy::HTMLSelectElement

Inherits:
HTMLElement show all
Defined in:
lib/dommy/html_elements.rb

Overview

‘<select>` — exposes `value` (selected option’s value), ‘options`, `selectedIndex`, and dispatches change events. Minimal compared to happy-dom’s full HTMLSelectElement, but covers common test cases.

Constant Summary

Constants inherited from Element

Element::ATTRIBUTE_NODE, Element::CDATA_SECTION_NODE, Element::COMMENT_NODE, Element::DOCUMENT_FRAGMENT_NODE, Element::DOCUMENT_NODE, Element::DOCUMENT_POSITION_CONTAINED_BY, Element::DOCUMENT_POSITION_CONTAINS, Element::DOCUMENT_POSITION_DISCONNECTED, Element::DOCUMENT_POSITION_FOLLOWING, Element::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, Element::DOCUMENT_POSITION_PRECEDING, Element::DOCUMENT_TYPE_NODE, Element::ELEMENT_NODE, Element::HTML_NAMESPACE, Element::PROCESSING_INSTRUCTION_NODE, Element::REFLECTED_TOKEN_LIST_HOSTS, Element::SHADOW_HOST_TAGS, Element::SVG_NAMESPACE, Element::TEXT_NODE

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::HTML_NAMESPACE, Node::PROCESSING_INSTRUCTION_NODE, Node::TEXT_NODE

Instance Attribute Summary

Attributes inherited from Element

#document

Instance Method Summary collapse

Methods inherited from HTMLElement

#case_sensitive_attribute_names?

Methods included from Internal::ReflectedAttributes

included

Methods inherited from Element

#[], #[]=, #__dommy_backend_node__, #__internal_set_namespace__, #__internal_shadow_root__, #__test_scroll_log__, #access_key_label, #after, #anchor_href, #animate, #aria_content_attr, #aria_element_attr, #aria_element_get, #aria_element_set, #aria_elements_attr, #aria_elements_get, #aria_elements_set, #aria_find_in_root, #aria_get, #aria_set, #at_xpath, #attach_shadow, #attributes, #base_uri, #before, #blur, #child_element_count, #child_nodes, #children, #class_list, #class_name, #class_name=, #clear_aria_element_ref_for, #click, #clone_node, #closest, #contains?, #dataset, #element_prefix, #equal_node?, #first_child, #first_element_child, #focus, #get_animations, #get_attribute, #get_attribute_names, #get_attribute_node, #get_attribute_node_ns, #get_attribute_ns, #get_bounding_client_rect, #get_client_rects, #get_elements_by_class_name, #get_elements_by_tag_name, #get_elements_by_tag_name_ns, #get_html, #get_inner_html, #has_attribute?, #has_attribute_ns?, #has_attributes?, #has_child_nodes?, #hide_popover, #id, #id=, #initialize, #inner_html, #inner_html=, #insert_adjacent_element, #insert_adjacent_html, #insert_adjacent_text, #insert_before, #insertion_parent!, #is_connected?, #last_child, #last_element_child, #live_child_nodes, #local_name, #matches?, #namespace_uri, #next_element_sibling, #next_sibling, #normalize, #on, #outer_html, #outer_html=, #owner_document, #parent_element, #parent_node, #path, #previous_element_sibling, #previous_sibling, #query_selector, #query_selector_all, #reflected_attr_name, #reflected_token_list, #remove, #remove_attribute, #remove_attribute_node, #remove_attribute_ns, #remove_child, #replace_child, #replace_with_nodes, #request_fullscreen, #role, #role=, #root_node, #same_node?, #scoped_query, #set_attribute, #set_attribute_node, #set_attribute_ns, #shadow_root, #show_popover, #slot, #slot=, #style, #tag_name, #text_content, #text_content=, #to_s, #toggle_attribute, #toggle_popover, #with_selector_errors, #xpath

Methods included from Bridge::Methods

included

Methods included from Internal::ParentNode

#append, #append_child, #prepend, #replace_children

Methods included from Node

#compare_document_position, #get_root_node, #is_default_namespace, #is_equal_node, #is_same_node, #lookup_namespace_uri, #lookup_prefix

Methods included from EventTarget

#__internal_deliver_event__, #__internal_event_parent__, #add_event_listener, capture_flag, #deliver_at, #dispatch_event, js_truthy?, #remove_event_listener

Constructor Details

This class inherits a constructor from Dommy::Element

Instance Method Details

#__js_call__(method, args) ⇒ Object



1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
# File 'lib/dommy/html_elements.rb', line 1663

def __js_call__(method, args)
  case method
  when "item"
    item(args[0])
  when "add"
    add(args[0], args[1])
  when "checkValidity"
    check_validity
  when "reportValidity"
    report_validity
  when "setCustomValidity"
    set_custom_validity(args[0])
  else
    super
  end
end

#__js_get__(key) ⇒ Object



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
# File 'lib/dommy/html_elements.rb', line 1622

def __js_get__(key)
  case key
  when "options"
    options
  when "length"
    length
  when "value"
    value
  when "size"
    size
  when "selectedIndex"
    selected_index
  when "form"
    form
  when "labels"
    labels
  when "type"
    type
  when "validity"
    validity
  when "willValidate"
    will_validate
  when "validationMessage"
    validation_message
  else
    super
  end
end

#__js_set__(key, val) ⇒ Object



1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
# File 'lib/dommy/html_elements.rb', line 1651

def __js_set__(key, val)
  case key
  when "value"
    self.value = val
  when "selectedIndex"
    self.selected_index = val
  else
    super
  end
end

#add(option, before = nil) ⇒ Object

‘select.add(option, before)` — appends or inserts before `before`.



1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
# File 'lib/dommy/html_elements.rb', line 1559

def add(option, before = nil)
  return nil unless option.respond_to?(:__dommy_backend_node__)

  if before.respond_to?(:__dommy_backend_node__)
    insert_before(option, before)
  else
    append_child(option)
  end

  nil
end

#check_validityObject



1607
1608
1609
1610
1611
# File 'lib/dommy/html_elements.rb', line 1607

def check_validity
  ok = !will_validate || validity.valid
  dispatch_event(Event.new("invalid", "bubbles" => false, "cancelable" => true)) unless ok
  ok
end

#formObject



1513
1514
1515
# File 'lib/dommy/html_elements.rb', line 1513

def form
  closest("form")
end

#item(i) ⇒ Object

‘select.item(i)` — returns the option at index i.



1554
1555
1556
# File 'lib/dommy/html_elements.rb', line 1554

def item(i)
  options[i.to_i]
end

#labelsObject



1579
1580
1581
1582
1583
# File 'lib/dommy/html_elements.rb', line 1579

def labels
  return [] if id.empty?

  @document.query_selector_all("label[for='#{id}']")
end

#lengthObject



1509
1510
1511
# File 'lib/dommy/html_elements.rb', line 1509

def length
  options.size
end

#optionsObject

‘options` — all <option> descendants (including those inside <optgroup>). Live HTMLOptionsCollection (HTMLCollection + add/remove/selectedIndex/length= helpers).



1487
1488
1489
1490
1491
1492
# File 'lib/dommy/html_elements.rb', line 1487

def options
  el = self
  HTMLOptionsCollection.new(self) do
    el.__dommy_backend_node__.css("option").map { |n| el.document.wrap_node(n) }.compact
  end
end

#remove_option(i) ⇒ Object

‘select.remove(i)` — removes the option at index i. (Note: also inherits `remove()` from ChildNode for self-removal; spec lets both forms coexist via overloading.)



1574
1575
1576
1577
# File 'lib/dommy/html_elements.rb', line 1574

def remove_option(i)
  target = options[i.to_i]
  target&.remove
end

#report_validityObject



1613
1614
1615
# File 'lib/dommy/html_elements.rb', line 1613

def report_validity
  check_validity
end

#selected_indexObject

‘selectedIndex` — first option with `selected`, or 0 if none and not multiple, or -1 if multiple and none.



1519
1520
1521
1522
1523
1524
1525
# File 'lib/dommy/html_elements.rb', line 1519

def selected_index
  opts = options
  idx = opts.find_index { |o| o.__dommy_backend_node__.key?("selected") }
  return idx if idx

  multiple ? -1 : (opts.empty? ? -1 : 0)
end

#selected_index=(i) ⇒ Object



1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
# File 'lib/dommy/html_elements.rb', line 1527

def selected_index=(i)
  opts = options
  opts.each_with_index do |o, idx|
    if idx == i.to_i
      o.set_attribute("selected", "")
    elsif o.__dommy_backend_node__.key?("selected")
      o.remove_attribute("selected")
    end
  end
end

#selected_optionsObject

‘selectedOptions` — live collection of options with `selected` attribute. When nothing is explicitly selected, browsers fall back to the first option for non-multiple selects.



1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
# File 'lib/dommy/html_elements.rb', line 1497

def selected_options
  el = self
  HTMLCollection.new do
    opts = el.__dommy_backend_node__.css("option").map { |n| el.document.wrap_node(n) }.compact
    chosen = opts.select { |o| o.__dommy_backend_node__.key?("selected") }
    next chosen unless chosen.empty?
    next [] if el.multiple

    opts.first ? [opts.first] : []
  end
end

#set_custom_validity(msg) ⇒ Object



1617
1618
1619
1620
# File 'lib/dommy/html_elements.rb', line 1617

def set_custom_validity(msg)
  @custom_validity_message = msg.to_s
  nil
end

#sizeObject

Own js_call methods, on top of Element’s.



1480
1481
1482
# File 'lib/dommy/html_elements.rb', line 1480

def size
  @__node__["size"].to_s.to_i
end

#typeObject



1585
1586
1587
# File 'lib/dommy/html_elements.rb', line 1585

def type
  multiple ? "select-multiple" : "select-one"
end

#validation_messageObject



1597
1598
1599
1600
1601
1602
1603
1604
1605
# File 'lib/dommy/html_elements.rb', line 1597

def validation_message
  return "" unless will_validate

  msg = (@custom_validity_message || "").to_s
  return msg unless msg.empty?
  return "Please select an item in the list." if validity.value_missing

  ""
end

#validityObject



1589
1590
1591
# File 'lib/dommy/html_elements.rb', line 1589

def validity
  @__validity ||= ValidityState.new(self)
end

#valueObject

‘value` of the select = value of the selected option, or “”.



1539
1540
1541
1542
1543
# File 'lib/dommy/html_elements.rb', line 1539

def value
  opts = options
  sel = opts.find { |o| o.__dommy_backend_node__.key?("selected") } || opts.first
  sel ? (sel.__dommy_backend_node__["value"] || sel.text_content).to_s : ""
end

#value=(new_value) ⇒ Object



1545
1546
1547
1548
1549
1550
1551
# File 'lib/dommy/html_elements.rb', line 1545

def value=(new_value)
  target = options.find { |o| (o.__dommy_backend_node__["value"] || o.text_content).to_s == new_value.to_s }
  return unless target

  options.each { |o| o.remove_attribute("selected") if o.__dommy_backend_node__.key?("selected") }
  target.set_attribute("selected", "")
end

#will_validateObject



1593
1594
1595
# File 'lib/dommy/html_elements.rb', line 1593

def will_validate
  !reflected_boolean("disabled")
end