1. Introduction
1.1. Motivation
Build-in elements provided by user agents have certain “states” that can change over time depending on user interaction and other factors, and are exposed to web authors through pseudo classes. For example, some form controls have the “invalid” state, which is exposed through the :invalid pseudo-class.
Like built-in elements, custom elements can have various states to be in too, and custom element authors want to expose these states in a similar fashion as the built-in elements.
1.2. Solution
This specification defines an API to inform custom element's states to the
user agent, and a pseudo-class to select elements with specific states.
The former is the states
IDL attribute of ElementInternals
, and the latter is the :state() pseudo-class.
Assume that LabeledCheckbox
doesn’t expose its "checked" state
via a content attribute.
<!DOCTYPE html> < body > <!-- Basic usage: --> < script > class LabeledCheckboxextends HTMLElement{ constructor() { super (); this . _internals= this . attachInternals(); this . addEventListener( 'click' , this . _onClick. bind( this )); const shadowRoot= this . attachShadow({ mode: 'closed' }); shadowRoot. innerHTML= `<style> :host::before { content: '[ ]'; white-space: pre; font-family: monospace; } :host(:state(checked))::before { content: '[x]' } </style> <slot>Label</slot>` ; } get checked() { return this . _internals. states. contains( 'checked' ); } set checked( flag) { this . _internals. states. toggle( 'checked' , !! flag); } _onClick( event) { this . checked= ! this . checked; } } customElements. define( 'labeled-checkbox' , LabeledCheckbox); </ script > < style > labeled-checkbox { border : dashed red ; } labeled-checkbox : state ( checked ) { border : solid ; } </ style > < labeled-checkbox > You need to check this</ labeled-checkbox > < script > class QuestionBoxextends HTMLElement{ constructor() { super (); const shadowRoot= this . attachShadow({ mode: 'closed' }); shadowRoot. innerHTML= `<div><slot>Question</slot></div> <labeled-checkbox part='checkbox'>Yes</labeled-checkbox>` ; } } customElements. define( 'question-box' , QuestionBox); </ script > < style > question-box :: part ( checkbox ) { color : red ; } question-box :: part ( checkbox ) : state ( checked ) { color : green ; } </ style > < question-box > Continue?</ question-box > </ body >
2. Exposing custom element states
Each autonomous custom element has states token list, a non-attribute DOMTokenList
object associated with the custom
element, and initially associated with an empty set of strings.
partial interface ElementInternals { [SameObject ,PutForwards =value ]readonly attribute DOMTokenList ; };
states
The states
IDL attribute returns the states token list of
this’s target element.
readyState
with "loading"
, "interactive"
, and "complete"
values can be mapped to three exclusive boolean states, "loading"
, "interactive"
, and "complete"
.
// Change the readyState from anything to "complete". this . _readyState= "complete" ; this . _internals. states
. remove
( "loading" , "interactive" ); this . _internals. states
. add
( "complete" ); // If this has no states other than _readyState, the following also works in // addition to remove() and add(). // this._internals.
states = "complete";
2.1. Non-attribute DOMTokenList
This section defines a variant of DOMTokenList
, called non-attribute DOMTokenList
. It is defined by [DOM] as
if following edits were applied to the definition of DOMTokenList
.
-
Replace this paragraph:
A
DOMTokenList
object also has an associated element and an attribute’s local name.with:
A
DOMTokenList
object also has an associated element and an optional attribute’s local name. If theDOMTokenList
object has an attribute’s local name, then it is known as an attribute-associatedDOMTokenList
; otherwise it is a non-attributeDOMTokenList
. -
Replace this paragraph:
A
DOMTokenList
object’s update steps are:- If the associated element does not have an associated attribute and token set is empty, then return.
- Set an attribute value for the associated element using associated attribute’s local name and the result of running the ordered set serializer for token set.
with:
A
DOMTokenList
object’s update steps are:- If the object has no attribute’s local name, then return.
- If the associated element does not have an associated attribute and token set is empty, then return.
- Set an attribute value for the associated element using associated attribute’s local name and the result of running the ordered set serializer for token set.
-
Replace this paragraph:
A
DOMTokenList
object’s serialize steps are to return the result of running get an attribute value given the associated element and the associated attribute’s local name.with:
A
DOMTokenList
object’s serialize steps are to return the result of running get an attribute value given the associated element and the associated attribute’s local name if the object has attribute’s local name. Otherwise, return the result of running the ordered set serializer for token set. -
Replace this paragraph:
A
DOMTokenList
object has these attribute change steps for its associated element:- If localName is associated attribute’s local name, namespace is null, and value is null, then empty token set.
- Otherwise, if localName is associated attribute’s local name, namespace is null, then set token set to value, parsed.
with:
A
DOMTokenList
object has these attribute change steps for its associated element:- If the object has no attribute’s local name, then return.
- If localName is associated attribute’s local name, namespace is null, and value is null, then empty token set.
- Otherwise, if localName is associated attribute’s local name, namespace is null, then set token set to value, parsed.
-
Replace this paragraph:
Setting the value attribute must set an attribute value for the associated element using associated attribute’s local name and the given value.
with:
Setting the value attribute must set an attribute value for the associated element using associated attribute’s local name and the given value if this has attribute’s local name. Otherwise, it must set token set to the given value, parsed.
3. Selecting a custom element with a speicfic state
The :state() pseudo-class applies while an element has a certain state. "State" is a per-element information which can change over time depending on user interaction and other extrinsic factors. The :state() pseudo-classs must have one <ident> argument, otherwise the selector is invalid.
The :state() pseudo-class must match any element that is an autonomous custom element and whose states token list contains the specified <ident>.