First, introduce the `PersistableElement` type to incorporate `<input>`,
`<textarea>`, and `<select>` elements.
Next, incorporate some special handling for `HTMLSelectElement`, since
they don't have a `.defaultValue` property.
Support for `HTMLSelectElement` instances is generalized for both
single and `[multiple]` elements by looping over
[HTMLSelectElement.selectedOptions][] while persisting, then setting
[HTMLSelectElement.selected][] when restoring.
[HTMLSelectElement.selectedOptions]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/selectedOptions
[HTMLSelectElement.selected]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionElement#instance_properties
By default, calls to `persistResumableFields()` will query the
`document` for elements with the (default or provided) `selector:`.
This commit adds support for specifying a `fields:` option, collecting
`NodeList` or `Node[]`, to provide the function with a set of `<input>`
or `<textarea>` elements to persist. Providing `fields:` would make
`selector:` redundant, and vice-versa.
```js
const myFields = document.querySelector("[data-persisted-field]")
// ...
// pass the `NodeList` directly
persistResumableFields(getPageID(), { fields: myFields })
// pass an array of `Node` instances
persistResumableFields(getPageID(), { fields: Array.from(myFields) })
```
Similarly, calls to `persistResumableFields()` will query the _document_
for elements with the (default or provided) `selector:` by default.
This commit adds support for specifying a `scope:` option to declare a
`ParentNode` instance other than the `document`.
```js
persistResumableFields(getPageID(), {
scope: document.getElementById("my-scope"),
selector: ".descendants-of-my-scope",
})
```
Introduce support for a `{ storageFilter: (field) => boolean }`
configuration override for calls to `persistResumableFields`. By
default, preserve the existing behavior of rejecting fields where
[value][] is equivalent to [defaultValue][].
This change is motivated by situations where an element's value is
impacted by events or packages that change its `[value]` attribute
directly.
For example, consider the following sample code:
```html
<input id="input" type="hidden" value="the default value">
<output id="output"></output>
<button>Change</button>
<script>
const button = document.querySelector("button")
const input = document.getElementById("input")
const output = document.getElementById("output")
output.textContent = input.defaultValue
button.addEventListener("click", () => {
input.setAttribute("value", "a new default value")
output.textContent = input.defaultValue
})
</script>
```
You can experiment with the code on [JSFiddle][].
Clicking the button changes the `<input>` element's `[value]` directly,
which has a side-effect of change its `.defaultValue` as well.
Within the context of `session-resume`, this means that any elements
impacted by side-effects of other code on the page will always be
omitted from being stored.
This is especially incompatible with `<trix-editor>` elements provided
by [Trix][] and [Action Text][].
[Trix]: https://trix-editor.org
[Action Text]: https://edgeguides.rubyonrails.org/action_text_overview.html
[value]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#value
[defaultValue]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultvalue
[JSFiddle]: https://jsfiddle.net/1nwb4o23/