◐ Shell
reader mode source ↗
Skip to content
Merged
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 5

---

# Add an option to select

There's a `<select>`:

```html
<select id="genres">
Expand All @@ -13,10 +13,10 @@ There's a `<select>`:
</select>
```

Use JavaScript to:

1. Show the value and the text of the selected option.
2. Add an option: `<option value="classic">Classic</option>`.
3. Make it selected.

Note, if you've done everything right, your alert should show `blues`.
169 changes: 85 additions & 84 deletions 2-ui/4-forms-controls/1-form-elements/article.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
# Form properties and methods

Forms and control elements, such as `<input>` have a lot of special properties and events.

Working with forms will be much more convenient when we learn them.

## Navigation: form and elements

Document forms are members of the special collection `document.forms`.

That's a so-called *"named collection"*: it's both named and ordered. We can use both the name or the number in the document to get the form.

```js no-beautify
document.forms.my; // the form with name="my"
document.forms[0]; // the first form in the document
```

When we have a form, then any element is available in the named collection `form.elements`.

For instance:

```html run height=40
<form name="my">
Expand All @@ -26,19 +27,19 @@ For instance:
</form>

<script>
// get the form
let form = document.forms.my; // <form name="my"> element

// get the element
let elem = form.elements.one; // <input name="one"> element

alert(elem.value); // 1
</script>
```

There may be multiple elements with the same name. This is typical with radio buttons and checkboxes.

In that case, `form.elements[name]` is a *collection*. For instance:

```html run height=40
<form>
Expand All @@ -57,11 +58,11 @@ alert(ageElems[0]); // [object HTMLInputElement]
</script>
```

These navigation properties do not depend on the tag structure. All control elements, no matter how deep they are in the form, are available in `form.elements`.


````smart header="Fieldsets as \"subforms\""
A form may have one or many `<fieldset>` elements inside it. They also have `elements` property that lists form controls inside them.

For instance:

Expand All @@ -81,57 +82,57 @@ For instance:
let fieldset = form.elements.userFields;
alert(fieldset); // HTMLFieldSetElement

// we can get the input by name both from the form and from the fieldset
alert(fieldset.elements.login == form.elements.login); // true
*/!*
</script>
</body>
```
````

````warn header="Shorter notation: `form.name`"
There's a shorter notation: we can access the element as `form[index/name]`.

In other words, instead of `form.elements.login` we can write `form.login`.

That also works, but there's a minor issue: if we access an element, and then change its `name`, then it is still available under the old name (as well as under the new one).

That's easy to see in an example:

```html run height=40
<form id="form">
<input name="login">
</form>

<script>
alert(form.elements.login == form.login); // true, the same <input>

form.login.name = "username"; // change the name of the input

// form.elements updated the name:
alert(form.elements.login); // undefined
alert(form.elements.username); // input

*!*
// form allows both names: the new one and the old one
alert(form.username == form.login); // true
*/!*
</script>
```

That's usually not a problem, however, because we rarely change names of form elements.

````

## Backreference: element.form

For any element, the form is available as `element.form`. So a form references all elements, and elements reference the form.

Here's the picture:

![](form-navigation.svg)

For instance:

```html run height=40
<form id="form">
Expand All @@ -151,64 +152,64 @@ For instance:

## Form elements

Let's talk about form controls.

### input and textarea

We can access their value as `input.value` (string) or `input.checked` (boolean) for checkboxes and radio buttons.

Like this:

```js
input.value = "New value";
textarea.value = "New text";

input.checked = true; // for a checkbox or radio button
```

```warn header="Use `textarea.value`, not `textarea.innerHTML`"
Please note that even though `<textarea>...</textarea>` holds its value as nested HTML, we should never use `textarea.innerHTML` to access it.

It stores only the HTML that was initially on the page, not the current value.
```

### select and option

A `<select>` element has 3 important properties:

1. `select.options` -- the collection of `<option>` subelements,
2. `select.value` -- the *value* of the currently selected `<option>`,
3. `select.selectedIndex` -- the *number* of the currently selected `<option>`.

They provide three different ways of setting a value for a `<select>`:

1. Find the corresponding `<option>` element (e.g. among `select.options`) and set its `option.selected` to `true`.
2. If we know a new value: set `select.value` to the new value.
3. If we know the new option number: set `select.selectedIndex` to that number.

Here is an example of all three methods:

```html run
<select id="select">
<option value="apple">Apple</option>
<option value="pear">Pear</option>
<option value="banana">Banana</option>
</select>

<script>
// all three lines do the same thing
select.options[2].selected = true;
select.selectedIndex = 2;
select.value = 'banana';
// please note: options start from zero, so index 2 means the 3rd option.
</script>
```

Unlike most other controls, `<select>` allows to select multiple options at once if it has `multiple` attribute. This attribute is rarely used, though.

For multiple selected values, use the first way of setting values: add/remove the `selected` property from `<option>` subelements.

Here's an example of how to get selected values from a multi-select:

```html run
<select id="select" *!*multiple*/!*>
Expand All @@ -218,7 +219,7 @@ Here's an example of how to get selected values from a multi-select:
</select>

<script>
// get all selected values from multi-select
let selected = Array.from(select.options)
.filter(option => option.selected)
.map(option => option.value);
Expand All @@ -227,72 +228,72 @@ Here's an example of how to get selected values from a multi-select:
</script>
```

The full specification of the `<select>` element is available in the specification <https://html.spec.whatwg.org/multipage/forms.html#the-select-element>.

### new Option

In the [specification](https://html.spec.whatwg.org/multipage/forms.html#the-option-element) there's a nice short syntax to create an `<option>` element:

```js
option = new Option(text, value, defaultSelected, selected);
```

This syntax is optional. We can use `document.createElement('option')` and set attributes manually. Still, it may be shorter, so here are the parameters:

- `text` -- the text inside the option,
- `value` -- the option value,
- `defaultSelected` -- if `true`, then `selected` HTML-attribute is created,
- `selected` -- if `true`, then the option is selected.

The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`, while `selected` sets whether the option is selected or not.

In practice, one should usually set _both_ values to `true` or `false`. (Or, simply omit them; both default to `false`.)

For instance, here's a new "unselected" option:

```js
let option = new Option("Text", "value");
// creates <option value="value">Text</option>
```

The same option, but selected:

```js
let option = new Option("Text", "value", true, true);
```

Option elements have properties:

`option.selected`
: Is the option selected.

`option.index`
: The number of the option among the others in its `<select>`.

`option.text`
: Text content of the option (seen by the visitor).

## References

- Specification: <https://html.spec.whatwg.org/multipage/forms.html>.

## Summary

Form navigation:

`document.forms`
: A form is available as `document.forms[name/index]`.

`form.elements`
: Form elements are available as `form.elements[name/index]`, or can use just `form[name/index]`. The `elements` property also works for `<fieldset>`.

`element.form`
: Elements reference their form in the `form` property.

Value is available as `input.value`, `textarea.value`, `select.value`, etc. (For checkboxes and radio buttons, use `input.checked` to determine whether a value is selected.)

For `<select>`, one can also get the value by the index `select.selectedIndex` or through the options collection `select.options`.

These are the basics to start working with forms. We'll meet many examples further in the tutorial.

In the next chapter we'll cover `focus` and `blur` events that may occur on any element, but are mostly handled on forms.
Toggle all file notes Toggle all file annotations