[whatwg/dom] Proposal: Enable Custom Form Controls to (Optionally) Conceal Internal/Irrelevant Events (Issue #1368)

ITenthusiasm created an issue (whatwg/dom#1368)

## What problem are you trying to solve?

Currently, there is no way for a Custom Form Control composed of primitive form controls to prevent "the outside world" from receiving undesirable/noisy events from said primitives. Consider the following example:

## Use Case: Comboboxes (where only valid values are accepted)
Many `combobox` components in the wild effectively act as stylable `<select>` elements whose restricted values can be conveniently searched via a textbox. In such cases, the `combobox`'s value is only permitted to be empty or a valid `option` value -- just as in the `<select>`'s case.

From an event dispatching standpoint, the `<select>` element only dispatches `input`/`change` events when a user interaction updates the value. Similarly, many of these `combobox`es desire to only dispatch an `input`/`change` event when a new _valid_ value is selected. And this is where things get tricky...

Typically, searchable `combobox`es are implemented with a native `<input>` (or some other `contenteditable` element). But these emit `input` events on every keystroke -- creating undesirable noise for delegated event listeners which are trying to watch for updates to the Combobox Value _only_. Particularly, the delegated event listeners will see `input` events whenever the textbox is typed into, **_and_** whenever a valid `option` is selected (in which case the `Combobox` will dispatch its own custom `input` event). But ideally, these events should only be detected/observed in the latter case to avoid noise/confusion.

There is currently no way to prevent this undesirable behavior. Even if someone tried to use a Shadow DOM, both `open` and `closed` Shadow DOMs will propagate `input` events to the Light DOM because `input` is `composed`. But `closed` Shadow DOMs will yield a slightly worse UX because they make it impossible to know whether the `input` came from the `Combobox` itself or the inner `input`. (I guess technically, someone could check `event.isTrusted`, but that requires knowledge of implementation details.)

(Sidenote for Clarity: the Combobox value should not be updated until the user selects a valid `option`. If the user bails out while searching, the textfield should revert its text content back to the last valid value of the Combobox, and the Combobox should not emit an `input`/`change` event. This prevents bad data from being sent to the server.)

Depending on how a `<checkbox-group>` component with `minimum-selected` items would be implemented, it might run into similar issues.

### What solutions exist today?

There technically aren't any real/genuine solutions to this problem today. Probably the best thing that developers can do is tell users how to filter out the events that they don't want in their delegated event listeners. However, this will almost certainly entail exposing implementation details, which is not optimal. For example, a developer could say, "Check for `event.isTrusted === false` or `event instanceof CustomEvent` to distinguish between the correct `input`/`change` events."

Perhaps another option is for the developer to create very clever listeners for `keydown` (composed textboxes), `click` (composed checkboxes), and the like which could call `event.preventDefault()` as needed. But it's possible that this approach could get out of hand quickly. And it might not cover all use cases.

### How would you solve it?

I've noticed that a number of new ShadowDOM-related attributes/options have appeared in the spec to resolve problems like these. Perhaps a new one could be created for this scenario? Something like:

```js
this.attachShadow({ mode: "closed", concealsEvents: true });
```
or
```html
<template shadowrootmode="closed" shadowrootconcealsevents>
```

Another alternative is perhaps to enable `ElementInternals` to specify that it conceals its own events through a togglable property.

```js
const internals = this.attachInternals();
internals.concealsEvents = true;
```

Could also be a static property, perhaps, like `formAssociated` and `observedAttributes`.

```js
class ComboboxField extends HTMLElement {
  static get concealedEvents() {
    return ["input", "change"];
  }

  // ...
}
```

The main idea is that in some way, the Web Component would be able to dictate that it conceals its events (or a specific set of events) within the class declaration.

### Anything else?

After writing up this issue, I'm starting to conclude that the issue may surround Custom Form Controls and the events which they want to emit more than it does the Shadow DOM. In simple terms, the issue is that a Custom Form Control may wish to take **_full_** responsibility over a specific set of events, because the events are irrelevant/noisy if they bubble up from any children (e.g., `input`/`change` in the combobox scenario).

Whether it's related to this problem or not, I feel like I can still make this comparison: Developers can't listen to `click` events on `<option>` elements in a `<select>` element -- nor do they need to. All that they really need to listen for is `input`/`change` from the `<select>` element itself. A sophisticated Custom Form Control leveraging "subcomponents"/"primitive controls" may require similar behavior.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/dom/issues/1368
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/dom/issues/1368@github.com>

Received on Wednesday, 9 April 2025 23:04:12 UTC