8

Here is some output from the console that illustrates my question

var a=document.createElement("select"); <ENTER>
undefined

a.appendChild(document.createElement("option"));  <ENTER>
<option>​</option>​

a
<select>​…​</select>​

a.options
[<option>​</option>​]

a.options[0];
<option>​</option>​

So far, so good. But now

I type a.options. and I am to type forEach but I notice forEach isn't getting listed.

 a.options.forEach(function() {});
VM1048:2 Uncaught TypeError: a.options.forEach is not a function
    at <anonymous>:2:11
    at Object.InjectedScript._evaluateOn (<anonymous>:905:140)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:838:34)
    at Object.InjectedScript.evaluate (<anonymous>:694:21)

Yet a.options so looked like an array

And forEach definitely works for arrays, no error.

a=[1,2];
[1, 2]
typeof a
"object"
a.forEach(function(){});
undefined

I guess the options of a selection box maybe aren't an array.. so what are they?

I've heard of the 'arguments' pseudo-array.. I guess perhaps a selection box's 'options' is like that? / some object that has similar syntax to array?

barlop
  • 12,887
  • 8
  • 80
  • 109
  • further to user286's answer https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement – barlop Aug 10 '15 at 01:10

3 Answers3

16

Because options is not an Array; it is an HTMLCollection. As such it does not have a forEach function-member.

The HTMLCollection interface represents a generic collection (array-like object) of elements (in document order) and offers methods and properties for selecting from the list.

One could use call with Array.prototype.forEach, since an HTMLCollection is array-like1:

Array.prototype.forEach.call(options, function ..)

1 The HTMLCollection interface has a length property and allows positional access via the indexer.

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • Is there a way to view that it is HTMLCollection , in code? (since I see that typeof just says object). – barlop Aug 10 '15 at 14:13
  • @barlop I'd open up another question for that; I'm not sure of a "good" way to do so. What about seeing if it's array-like but *not* an array? – user2864740 Aug 10 '15 at 19:27
  • http://stackoverflow.com/questions/7893776/the-most-accurate-way-to-check-js-object-type var a=document.getElementsByTagName("*"); Object.prototype.toString.call(a); – barlop Aug 10 '15 at 20:05
  • @barlop Whoops. I messed up that syntax. In any case I would still recommend *not* 'checking for' the specific type but instead rely on [duck typing](https://en.wikipedia.org/wiki/Duck_typing) and proper adherence to contractual documentation. For instance, for the options, as in the original question, Chrome yields "[object HTMLOptionsCollection]" instead of the more generic "HTMLCollection". Furhermore, I have no idea if these results are consistent across browsers (I have a feeling it depends a good bit on how the DOM API is exposed, by W3C and implementation). – user2864740 Aug 10 '15 at 21:23
  • @barlop Basically there doesn't appear to be a need to tell what 'type' of collection it is; other than perhaps if it is *not* a proper array. Contractual method documentation can further mandate either an "array" or an "array-like" object pushing any required conversion process into the caller and handled as a suitable type of duck at the usage site. – user2864740 Aug 10 '15 at 21:25
4

The answer given by user2864740 is excellent.

I will give an example of how to get access to options based on the provided solution and display text content of all of them in the console:

var select = $('#selector_here');
Array.prototype.forEach.call(select.options, function(option, index) {
  console.log('option ' + (index + 1) + ':', option.textContent);
  /* do some required magic here */
});
Mario Boss
  • 1,784
  • 3
  • 20
  • 43
0

You can do:

Object.values(document.getElementById("myElementID").options).forEach(element => console.log(element));
Kevin O.
  • 355
  • 3
  • 11