-
Notifications
You must be signed in to change notification settings - Fork 278
Add dev trial HOWTO for platform-provided behaviors #1313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
leotlee
merged 2 commits into
MicrosoftEdge:main
from
anaskim:platform-provided-behaviors-devtrial-howto
May 11, 2026
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| # How to experiment with Platform-Provided Behaviors | ||
|
|
||
| Platform-provided behaviors allow custom elements to adopt native HTML element capabilities through `attachInternals()`. The initial behavior, `HTMLSubmitButtonBehavior`, gives custom elements the form submission capability of `<button type="submit">`. | ||
|
|
||
| **This is an experimental feature available in Microsoft Edge and Chrome Canary channels, subject to change as the specification develops.** | ||
|
|
||
| ## Enabling the feature | ||
|
|
||
| Navigate to `edge://flags/#enable-experimental-web-platform-features` (or `chrome://flags/#enable-experimental-web-platform-features`) and set it to **Enabled**, then restart the browser. | ||
|
|
||
| Alternatively, launch the browser with: | ||
| ``` | ||
| --enable-features=ElementInternalsBehaviors | ||
| ``` | ||
|
|
||
| ## Quick test | ||
|
|
||
| Open DevTools (F12) and paste the following in the Console: | ||
| ```javascript | ||
| typeof HTMLSubmitButtonBehavior !== 'undefined' | ||
| ? 'Platform-provided behaviors are available!' | ||
| : 'Not available. Check that the flag is enabled.'; | ||
| ``` | ||
|
|
||
| ## Basic example | ||
|
|
||
| Create a custom submit button that triggers form submission on click, keyboard activation (Space/Enter), and participates in implicit submission (Enter key inside the form): | ||
| ```html | ||
| <!DOCTYPE html> | ||
| <form action="/submit" method="post"> | ||
| <label> | ||
| Name: <input name="username" required> | ||
| </label> | ||
| <custom-submit-button>Submit</custom-submit-button> | ||
| </form> | ||
|
|
||
| <script> | ||
| class CustomSubmitButton extends HTMLElement { | ||
| static formAssociated = true; | ||
|
|
||
| constructor() { | ||
| super(); | ||
| this.attachShadow({ mode: 'open' }).innerHTML = ` | ||
| <style> | ||
| :host { | ||
| display: inline-block; | ||
| padding: 8px 16px; | ||
| border: 1px solid #333; | ||
| border-radius: 4px; | ||
| cursor: pointer; | ||
| user-select: none; | ||
| } | ||
| :host(:disabled) { opacity: 0.5; cursor: not-allowed; } | ||
| :host(:active) { background: #eee; } | ||
| </style> | ||
| <slot></slot> | ||
| `; | ||
| this._submitBehavior = new HTMLSubmitButtonBehavior(); | ||
| this._internals = this.attachInternals({ | ||
| behaviors: [this._submitBehavior] | ||
| }); | ||
| } | ||
| } | ||
| customElements.define('custom-submit-button', CustomSubmitButton); | ||
| </script> | ||
| ``` | ||
|
|
||
| What to observe: | ||
| - **Click the button**: the form submits. | ||
| - **Focus the button and press Space or Enter**: the form submits. | ||
| - **Focus the text input and press Enter**: implicit submission triggers via the custom submit button. | ||
| - **Inspect accessibility**: DevTools Accessibility pane shows `role="button"`. | ||
| - **Tab navigation**: the element participates in the tab order. | ||
|
|
||
| ## Form override properties | ||
|
|
||
| `HTMLSubmitButtonBehavior` exposes the same form override properties as a native `<button type="submit">`: | ||
| ```javascript | ||
| class CustomSubmitButton extends HTMLElement { | ||
| static formAssociated = true; | ||
| static observedAttributes = ['disabled', 'formaction', 'formmethod']; | ||
|
|
||
| constructor() { | ||
| super(); | ||
| this._submitBehavior = new HTMLSubmitButtonBehavior(); | ||
| this._internals = this.attachInternals({ | ||
| behaviors: [this._submitBehavior] | ||
| }); | ||
| } | ||
|
|
||
| attributeChangedCallback(name, oldValue, newValue) { | ||
| switch (name) { | ||
| case 'disabled': | ||
| this._submitBehavior.disabled = newValue !== null; | ||
| break; | ||
| case 'formaction': | ||
| this._submitBehavior.formAction = newValue ?? ''; | ||
| break; | ||
| case 'formmethod': | ||
| this._submitBehavior.formMethod = newValue ?? ''; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| get disabled() { | ||
| return this._submitBehavior.disabled; | ||
| } | ||
|
|
||
| set disabled(val) { | ||
| this._submitBehavior.disabled = val; | ||
| } | ||
| } | ||
| customElements.define('custom-submit-button', CustomSubmitButton); | ||
| ``` | ||
|
|
||
| ```html | ||
| <form action="/default" method="get"> | ||
| <input name="data" value="test"> | ||
| <!-- This button overrides the form action and method. --> | ||
| <custom-submit-button formaction="/override" formmethod="post"> | ||
| Save | ||
| </custom-submit-button> | ||
| </form> | ||
| ``` | ||
|
|
||
| ## Feature detection and fallback | ||
|
|
||
| ```javascript | ||
| class CustomSubmitButton extends HTMLElement { | ||
| static formAssociated = true; | ||
|
|
||
| constructor() { | ||
| super(); | ||
| if (typeof HTMLSubmitButtonBehavior !== 'undefined') { | ||
| this._submitBehavior = new HTMLSubmitButtonBehavior(); | ||
| this._internals = this.attachInternals({ | ||
| behaviors: [this._submitBehavior] | ||
| }); | ||
| } else { | ||
| // Fall back to manual event handling. | ||
| this._internals = this.attachInternals(); | ||
| this.addEventListener('click', () => { | ||
| this._internals.form?.requestSubmit(this); | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Disabled state | ||
|
|
||
| Setting `disabled` on the behavior removes the element from tab order and prevents activation: | ||
| ```javascript | ||
| button._submitBehavior.disabled = true; | ||
| console.log(button.matches(':disabled')); // true | ||
| ``` | ||
|
|
||
| The element is also disabled if the `disabled` attribute is present on the element itself (standard form control disabling) or if it's inside a `<fieldset disabled>`. | ||
|
|
||
| ## Inspecting behaviors in DevTools | ||
|
|
||
| ```javascript | ||
| const button = document.querySelector('custom-submit-button'); | ||
| const internals = button._internals; // If exposed by the component. | ||
| console.log(internals.behaviors); // FrozenArray [HTMLSubmitButtonBehavior] | ||
| console.log(internals.behaviors[0].formAction); // Current formAction value. | ||
| ``` | ||
|
|
||
| ## Key constraints | ||
|
|
||
| - Behaviors are **immutable after attachment**: you cannot add, remove, or replace behaviors after calling `attachInternals()`. | ||
| - A behavior instance can only be attached to **one element**. Reusing the same instance on another element throws `TypeError`. | ||
| - Attaching multiple instances of the **same behavior type** to one element throws `TypeError`. | ||
| - The element must be **form-associated** (`static formAssociated = true`) for form submission to work. Without it, activation is a no-op. | ||
|
|
||
| ## Filing bugs | ||
|
|
||
| If you encounter issues, please file a bug at https://issues.chromium.org/issues/new?component=1456278&blocking=486928684. | ||
|
|
||
| ## Resources | ||
|
|
||
| - [Explainer](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/PlatformProvidedBehaviors/explainer.md) | ||
| - [WHATWG tracking issue](https://github.com/whatwg/html/issues/12150) | ||
| - [Chromium bug](https://issues.chromium.org/issues/486928684) | ||
| - [Intent to Prototype](https://groups.google.com/a/chromium.org/g/blink-dev/c/ETKzYhB6BbI/m/0jQaazNHAQAJ) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.