This proposal introduces an API for defining custom attributes for both built-ins and custom elements, and design discussion for an API that can be used to define more complex enhancements that involve multiple attributes, methods, JS-only properties etc.
This came out of the TPAC extending built-ins breakout, and some follow-up discussions with @keithamus.
Defining attributes: TheAttribute
class Use cases
HTMLElement.attributeRegistry
property which is an AttributeRegistry object. Attributes can be registered generically on HTMLElement
to be available everywhere, or on specific classes (built-in or custom element classes).
aria-*
, SVG attributes etc). Or maybe this should be a validation restriction, and not actually enforced by the API?Attribute
should extend EventTarget
so that authors can dispatch events on it.Definition:
class ListAttribute extends Attribute { ownerElement; // element this is attached to value; // current value connectedCallback() { /* ... */ } disconnectedCallback() { /* ... */ } // Called whenever the attribute's value changes changedCallback() { /* ... */ } static dataType = AttributeType.IDREF; // Optional default value static defaultValue = null; }
Usage on built-ins or existing custom elements:
HTMLInputElement.attributeRegistry.define("ac-list", ListAttribute);
An optional options dictionary allows customizing the registration, such as:
propertyName
to override the automatic camelCase conversionMyInput.attributeRegistry.define("ac-list", ListAttribute, { propertyName: "autocompleteList", });
Usage on new custom elements:
class MyInput extends HTMLElement { ... } MyInput.attributeRegistry.define("ac-list", ListAttribute); MyInput.attributeRegistry.define("value", class ValueAttribute extends Attribute { dataType = AttributeType.NUMBER; defaultValue = 0; });
We could also add a new static attributes
property as syntactic sugar to simplify the code needed and keep the definition within the class:
class MyInput extends HTMLElement { // Or maybe an array? static attributes = { "ac-list": ListAttribute, // Creates a suitable Attribute subclass behind the scenes: value: { dataType: AttributeType.NUMBER, defaultValue: 0 } } }Types
In v0 types could only be predefined AttributeType
objects, in the future these should be constructible (all they need is a parse()
and stringify()
function, and maybe an optional defaultValue
to act as a base default value).
Complex enhancements include:
Attribute
objects)This can be fleshed out at a later time, since Attribute
already deals with a lot of use cases. That could give us time to get more data about what is needed.
Element behaviors use a has
attribute that takes a list of identifiers, and that is totally a viable path. The downside of this is that it introduces noise, and potential for error. E.g. imagine implementing a language like VueJS in this way, one would need to use has
in addition to any v-*
attribute, and would inevitably forget.
Associating enhancements with a selector would probably afford maximum flexibility and allows the association to happen implicitly (e.g. for an Enhancement implementing htmx, the "activation selector" could be [hx-get], [hx-post], [hx-swap], ...
, without an additional has="htmx"
being required eveyrwhere that these attributes are used.
Imperative association could allow us to explore functionality without committing to a particular declarative scheme. That way, individual attributes can still automatically activate behaviors, though it's a bit clunky:
class MyAttribute extends Attribute { connectedCallback () { this.ownerElement.behaviors.add(Htmx); } disconnectedCallback () { let hasOthers = this.ownerElement.getAttributeNames().some(n => n.startsWith("hx-"); if (!hasOthers) this.ownerElement.behaviors.delete(Htmx); } }Flexibility
Should enhancements allow the same separation of names and functionality as custom elements and attributes? Given that they include multiple attributes, how would the association happen? I'm leaning towards that they'd also name the attributes they are including (since they are naming properties, methods etc already anyway).
// Should have the same syntax as HTMLElement.attributes above attributes: { "v-if": VIf "v-else": VElse, "v-on": { value: VOn, propertyName: "vOnEvent" } ... }List of use cases
I have been maintaining a list of use cases that I periodically add to here, I will edit this periodically to import it:
Use cases for custom attributes
format
attribute on <time>
, <data>
for presenting data with custom formatsname
<button>
<button href>
prefix="icon-name"
/suffix="icon-name"
)<p loading-placeholder="3 sentences">
)highlight="foobar"
)removable
(adds X button that removes the element and fires a suitable event)<table sortable>
<td value>
to be used for filtering and sorting<pre>
<pre src>
<pre editable>
for code editors (or any other element)<pre normalize-whitespace>
<audio>
and <video>
)
start-at="0:05"
PatrickJS, DavidJCobb, DeepDoge, jvisker, radex02 and 7 morePatrickJS, o-t-w, DeepDoge and Jintusjohnyanarella, davatron5000, Mr0grog, Delapouite, EisenbergEffect and 22 morePatrickJS, DeepDoge, brandonmcconnell and Jintusbathos, jimmyfrasche, JulianCataldo, imacrayon, AramZS and 14 more
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4