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

Instance Attribute Summary

Attributes inherited from Element

#__node__, #document

Instance Method Summary collapse

Methods inherited from HTMLElement

#case_sensitive_attribute_names?

Methods inherited from Element

#[], #[]=, #__scroll_log__, #__shadow_root__, #after, #anchor_href, #animate, #append, #append_child, #attach_shadow, #attributes, #base_uri, #before, #blur, #child_element_count, #child_nodes, #children, #class_list, #class_name, #class_name=, #click, #clone_node, #closest, #compare_document_position, #contains?, #dataset, #equal_node?, #first_child, #first_element_child, #focus, #get_animations, #get_attribute, #get_attribute_node, #get_elements_by_class_name, #get_elements_by_tag_name, #get_html, #get_inner_html, #has_attribute?, #has_attributes?, #has_child_nodes?, #id, #id=, #initialize, #inner_html, #inner_html=, #insert_adjacent_element, #insert_adjacent_html, #insert_adjacent_text, #insert_before, #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, #prepend, #previous_element_sibling, #previous_sibling, #query_selector, #query_selector_all, #reflected_attr_name, #remove, #remove_attribute, #remove_attribute_node, #remove_child, #replace_child, #replace_children, #replace_with_nodes, #role, #role=, #root_node, #same_node?, #set_attribute, #set_attribute_node, #shadow_root, #slot, #slot=, #style, #tag_name, #text_content, #text_content=, #to_s, #toggle_attribute

Methods included from EventTarget

#__deliver_event__, #add_event_listener, #dispatch_event, #invoke_listener, #remove_event_listener

Constructor Details

This class inherits a constructor from Dommy::Element

Instance Method Details

#__js_call__(method, args) ⇒ Object



2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
# File 'lib/dommy/html_elements.rb', line 2348

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



2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
# File 'lib/dommy/html_elements.rb', line 2300

def __js_get__(key)
  case key
  when "options"
    options
  when "length"
    length
  when "value"
    value
  when "name"
    name
  when "multiple"
    multiple
  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



2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
# File 'lib/dommy/html_elements.rb', line 2333

def __js_set__(key, val)
  case key
  when "value"
    self.value = val
  when "name"
    set_reflected_string("name", val)
  when "multiple"
    set_reflected_boolean("multiple", 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`.



2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
# File 'lib/dommy/html_elements.rb', line 2237

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

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

  nil
end

#check_validityObject



2285
2286
2287
2288
2289
# File 'lib/dommy/html_elements.rb', line 2285

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

#formObject



2191
2192
2193
# File 'lib/dommy/html_elements.rb', line 2191

def form
  closest("form")
end

#item(i) ⇒ Object

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



2232
2233
2234
# File 'lib/dommy/html_elements.rb', line 2232

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

#labelsObject



2257
2258
2259
2260
2261
# File 'lib/dommy/html_elements.rb', line 2257

def labels
  return [] if id.empty?

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

#lengthObject



2187
2188
2189
# File 'lib/dommy/html_elements.rb', line 2187

def length
  options.size
end

#multipleObject



2150
2151
2152
# File 'lib/dommy/html_elements.rb', line 2150

def multiple
  reflected_boolean("multiple")
end

#multiple=(v) ⇒ Object



2154
2155
2156
# File 'lib/dommy/html_elements.rb', line 2154

def multiple=(v)
  set_reflected_boolean("multiple", v)
end

#nameObject



2142
2143
2144
# File 'lib/dommy/html_elements.rb', line 2142

def name
  reflected_string("name")
end

#name=(v) ⇒ Object



2146
2147
2148
# File 'lib/dommy/html_elements.rb', line 2146

def name=(v)
  set_reflected_string("name", v)
end

#optionsObject

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



2165
2166
2167
2168
2169
2170
# File 'lib/dommy/html_elements.rb', line 2165

def options
  el = self
  HTMLOptionsCollection.new(self) do
    el.__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.)



2252
2253
2254
2255
# File 'lib/dommy/html_elements.rb', line 2252

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

#report_validityObject



2291
2292
2293
# File 'lib/dommy/html_elements.rb', line 2291

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.



2197
2198
2199
2200
2201
2202
2203
# File 'lib/dommy/html_elements.rb', line 2197

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

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

#selected_index=(i) ⇒ Object



2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
# File 'lib/dommy/html_elements.rb', line 2205

def selected_index=(i)
  opts = options
  opts.each_with_index do |o, idx|
    if idx == i.to_i
      o.set_attribute("selected", "")
    elsif o.__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.



2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
# File 'lib/dommy/html_elements.rb', line 2175

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

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

#set_custom_validity(msg) ⇒ Object



2295
2296
2297
2298
# File 'lib/dommy/html_elements.rb', line 2295

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

#sizeObject



2158
2159
2160
# File 'lib/dommy/html_elements.rb', line 2158

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

#typeObject



2263
2264
2265
# File 'lib/dommy/html_elements.rb', line 2263

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

#validation_messageObject



2275
2276
2277
2278
2279
2280
2281
2282
2283
# File 'lib/dommy/html_elements.rb', line 2275

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



2267
2268
2269
# File 'lib/dommy/html_elements.rb', line 2267

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

#valueObject

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



2217
2218
2219
2220
2221
# File 'lib/dommy/html_elements.rb', line 2217

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

#value=(new_value) ⇒ Object



2223
2224
2225
2226
2227
2228
2229
# File 'lib/dommy/html_elements.rb', line 2223

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

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

#will_validateObject



2271
2272
2273
# File 'lib/dommy/html_elements.rb', line 2271

def will_validate
  !reflected_boolean("disabled")
end