Custom Elements

W3C Last Call Working Draft 24 October 2013

This version
http://www.w3.org/TR/2013/WD-custom-elements-20131024/
Latest version
http://www.w3.org/TR/custom-elements/
Previous version
http://www.w3.org/TR/2013/WD-custom-elements-20130514/
Latest editor's draft
https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
Revision history
https://dvcs.w3.org/hg/webcomponents/log/tip/spec/custom/index.html
Participate
Discuss on public-webapps@w3.org (Web Applications Working Group)
File bugs (w3.org's Bugzilla)
Editor
Dimitri Glazkov, Google, <>

Abstract

This specification describes the method for enabling the author to define and use new types of DOM elements in a document.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This is a Last Call Working Draft of "Custom Elements." The W3C Membership and other interested parties are invited to review the document and send comments to public-webapps@w3.org (with public archive) through 21 November 2013.

This document was developed by the Web Applications Working Group. The Working Group expects to advance this Working Draft to Recommendation Status.

Publication as a Last Call Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

Table of Contents

  1. 1 About this Document
  2. 2 Dependencies
  3. 3 Motivations
  4. 4 Concepts
  5. 5 Custom Element Lifecycle
    1. 5.1 Enqueueing and Invoking Callbacks
    2. 5.2 Types of Callbacks
  6. 6 Creating and Passing Registries
  7. 7 Registering Custom Elements
    1. 7.1 Extensions to Document Interface
    2. 7.2 Unresolved Element Pseudoclass
  8. 8 Instantiating Custom Elements
    1. 8.1 Extensions to Document Interface
  9. 9 Parsing Custom Elements
  10. 10 Appendix A: All Algorithms in One Diagram
  11. Acknowledgements

1 About this Document

All diagrams, examples, notes, are non-normative, as well as sections explicitly marked as non-normative. Everything else in this specification is normative.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this specification.

Any point, at which a conforming UA must make decisions about the state or reaction to the state of the conceptual model, is captured as algorithm. The algorithms are defined in terms of processing equivalence. The processing equivalence is a constraint imposed on the algorithm implementors, requiring the output of the both UA-implemented and the specified algorithm to be exactly the same for all inputs.

2 Dependencies

This document relies on the following specifications:

3 Motivations

There are two motivations that fueled the development of this specification:

  1. Provide a way for Web developers to build their own, fully-featured DOM elements. Though it was long possible to create DOM elements with any tag names in HTML, these elements weren't very functional. By giving Web developers the means to both inform the parser on how to properly construct an element and to react to lifecycle changes of an element, the specification eliminates the need for DOM-as-a-render-view scaffolding that has to exist today in most web frameworks or libraries.
  2. Rationalize the platform. The specification ensures that all of its new features and abilities are in concert with how the relevant bits of the Web platform work today, so that these new features could be used to explain the functionality of existing Web platform features, such as HTML elements.

Most of the effort went into finding the right balance between the two motivations, driven by the hope that these motivations do not run counter to each other, but are rather complementary parts of the same larger story. For example, though the scope of the spec is currently limited to only creating custom elements by authors, it is designed to shorten the distance to a much more ambitious goal of rationalizing all HTML, SVG, and MathML elements into one coherent system.

4 Concepts

Custom element is platform object whose interface is defined by the author. The interface prototype object of a custom element's interface is called the custom element prototype.

The custom element type identifies a custom element interface and is a sequence of characters that must match the NCName production and contain a U+002D HYPHEN-MINUS character. The custom element type must not be one of the following values:

The list of names above is the summary of all hyphen-containing element names from the applicable specifications, namely SVG and MathML.

The element definition describes a custom element and consists of:

At the time of creation, a document could be associated with a registry. A registry is a set of element definitions.

Element registration is a process of adding an element definition to a registry. One element definition can only be registered with one registry.

If a document has a registry associated with it, then for this document and a given element definition in the registry, the custom element's interface must be the element interface for local name and namespace values of custom element type and the namespace of the element definition, respectively.

Effectively, the registry is consulted whenever a new DOM element is created, whether imperatively or by a parser. Whenever a matching element definition is found in the registry, the information in this definition is used to create a new instance of a custom element.

If a document does not have a registry associated with it, all attempts at element registration will fail.

The exact nature of creating registries, their association with documents, and element registration are defined further in this specification.

5 Custom Element Lifecycle

A custom element can go through these changes during its lifetime:

Various callbacks can be invoked when a custom element goes through some of these changes. These callbacks are stored internally as a collection of key-value pairs and called lifecycle callbacks.

To transfer a callback named name from an object property named property to lifecycle callbacks, the user agent must run the following steps:

Input
NAME, name of the callback
PROPERTY, name of the property
OBJECT, the object from which PROPERTY is being transferred
LIFECYCLE, the lifecycle callbacks
Output
None
  1. Let CALLBACK be the result of getting a property named PROPERTY of OBJECT
  2. If CALLBACK exists and is a callable object, add CALLBACK to LIFECYCLE, associated with the key NAME.

5.1 Enqueuing and Invoking Callbacks

To facilitate invoking callbacks, each unit of related similar-origin browsing contexts has a processing stack, which is initially empty. Each item in the stack is an element queue, which is initially empty as well. Each item in the element queue is a custom element.

Each custom element has an associated callback queue and an element is being created flag. The flag is initially set to false and the callback queue is initially empty. Each item in the queue consists of the callback itself and zero or more string values that are used as callback arguments.

To invoke callbacks in an element queue, the user agent must run these steps or their equivalent:

Input
QUEUE, an element queue
Output
None
  1. For each custom element ELEMENT in QUEUE:
    1. Let CALLBACKS be the ELEMENT's callback queue
    2. Repeat until CALLBACKS is empty:
      1. Remove the first item from CALLBACKS and let CALLBACK be this item
      2. Invoke CALLBACK with ELEMENT as callback this value and, if present, using string values in CALLBACK as arguments

Any time a script calls a method, reads or sets a property that is implemented by the user agent, the following actions must occur:

As described, these actions wrap every user agent-implemented method or property accessor. The intended effect is that any lifecycle callbacks, enqueued as a result of running these methods or accessors are invoked prior to returning control back to script. If a method or accessor is known to never enqueue a lifecycle callback, the user agent could choose not to wrap it as a performance optimization.

In addition to an element queue, there is also a sorted element queue. The custom elements are kept in the order of increasing custom element order.

The custom element order is a sum of document custom element order and import tree order, in which the import tree order is scaled so that its lowest value is always larger than the highest possible value of document custom element order.

The document custom element order is a numerical value, associated with every custom element. This value is as a result of custom element's document keeping a numerical value that is incremented and assigned to custom element as its custom element order whenever the following occurs:

The import tree order of a given custom element of an import tree is determined by tree order in an import tree that was flattened by replacing every import link with the content of its imported document.

The highest stable order is the value that is immediately preceeding the custom element order of an element in the first encountered import, in tree order, whose ready flag is not yet set. If there is no such element, the highest stable order is the highest custom element order in the flattened import tree.

Because imports load asynchronously, we need to divide a sorted element queue into the part where things have settled down (all imports have loaded), and the part where the loading is still happening, and thus the actual sorting order is not yet determined. For example, suppose you have the following document structure:


index.html:
<me-first></me-first>
<link rel="import" href="import.html">
<me-third></me-third>

import.html:
<me-second></me-second>

The order of custom elements in the flattened import tree is me-first (1), me-second (2), me-third (3). However, it's very likely that the parser will find out about me-third sooner than me-second, since the latter requires loading the import.html. While the network stack is doing its job, the highest stable order stays at me-first (1). When import.html is ready, the order jumps all the way to me-third (3).

Each unit of related similar-origin browsing contexts has an initially-empty sorted element queue, called base element queue.

As part of the microtask checkpoint, the user agent must process base element queue for the unit of related similar-origin browsing contexts to which the scripts' browsing context belongs.

To prevent reentrance while processing base element queue, each unit of related similar-origin contexts has a processing base element queue flag, which must initially be false.

Currently, the HTML spec has sequential processing of steps in microtask checkpoint. Since every step may potentially generate additional microtasks for past steps, this may lead to incomplete fulfillment of microtask checkpoint objectives. This specification assumes that this deficiency has been addressed and adding a new step to microtask checkpoint guarantees exhaustion of microtasks for all types of steps.

To process base element queue, a conforming user agent must run the following steps or their equivalent:

Input
ENVIRONMENT, the unit of related similar-origin browsing contexts
Output
None
  1. Let PROCESSING be the processing base element queue flag
  2. If PROCESSING is true, stop.
  3. Set PROCESSING to true.
  4. Invoke callbacks in ENVIRONMENT's base element queue up to the highest stable order, inclusively
  5. Set PROCESSING to false.

In the unit of related similar-origin browsing contexts to which the scripts' browsing context belongs, the current element queue is the element queue at the top of the processing stack or the base element queue if the processing stack is empty.

To enqueue a lifecycle callback, the user must run the following steps or their equivalent:

Input
NAME, name of the callback
ELEMENT, the custom element for which the callback is enqueued
Output
None
  1. Let DEFINITION be ELEMENT's definition
  2. If DEFINITION does not exist, let CALLBACK be null and stop.
  3. Let CALLBACKS be the lifecycle callbacks from DEFINITION
  4. Let CALLBACK be the callback, associated with the key NAME in CALLBACKS
  5. If there is no such callback, stop.
  6. Add CALLBACK to ELEMENT's callback queue
  7. If element is being created flag is false, add ELEMENT to current element queue.

5.2 Types of Callbacks

The following callbacks are recognized:

created
This callback is invoked after custom element instance is created and its definition is registered. The actual timing of this callback is defined further in this specification.
The custom element prototype must be set just prior to invoking callback.
For the duration of this callback invocation, the element is being created flag must be set to true. In all other cases, the flag must be set to false.
All other callbacks must not be enqueued until after the created callback's invocation had started.
enteredView
Unless specified otherwise, this callback must be enqueued whenever custom element is inserted into a document and this document has a browsing context.
leftView
Unless specified otherwise, this callback must be enqueued whenever custom element is removed from the document and this document has a browsing context.
attributeChanged
Unless specified otherwise, this callback must be enqueued whenever custom element's attribute is added, changed or removed. Depending on the type of attribute modification, the following additional strings are added to the queue item:
attribute is set
attribute name, null and new attribute value
attribute is changed
attribute name, old attribute value and new attribute value
attribute is removed
attribute name, old attribute value and null

To set custom element prototype on a custom element, a conforming user agent must run the following steps or their equivalent:

Input
ELEMENT, element
Output
None
  1. Let PROTOTYPE be the custom element prototype in ELEMENT's definition
  2. Set the value of the [[Prototype]] internal property of ELEMENT to PROTOTYPE.
  3. If ELEMENT is in a document and this document has a browsing context:
    1. Enqueue enteredView callback for ELEMENT

6 Creating and Passing Registries

When an HTML Document is loaded in a browsing context, a new registry must be created and associated with this document.

A new document instance must be associated with an existing registry in these cases:

In all other cases, new documents must not have a registry.

7 Registering Custom Elements

Because element registration can occur at any time, a custom element could be created before its definition is registered. Such custom element instances are called unresolved elements. When an unresolved element is created, and if its element interface was not defined by HTML or other applicable specifications, the unresolved element's element interface must be:

The effect of this statement is that any HTML (or SVG) element with the local name that is a valid custom element type will have HTMLElement (or SVGElement) as element interface, rather than HTMLUnknownElement.

Each registry has an associated map of all instances of unresolved elements for a given pair of custom element type and namespace. This data structure is called the upgrade candidates map and is initially empty. Each value item in this map is a sorted element queue.

Whenever an unresolved element is created, it must be added to the respective sorted element queue in upgrade candidates map.

Registering an element definition is the responsibility of the element registration algorithm, which must be equivalent to running these steps:

Input
DOCUMENT, the document
TYPE, the custom element type of the element being registered
PROTOTYPE, the custom element prototype
NAME, a local name, optional
Output
ERROR, a variable that holds one of these values: None, InvalidType, InvalidName, NoRegistry, or DuplicateDefinition
  1. Let ERROR and DEFINITION be the result of running definition construction algorithm with DOCUMENT, TYPE, PROTOTYPE, and NAME as arguments
  2. If ERROR is not None, stop.
  3. Let REGISTRY be DOCUMENT's registry
  4. If REGISTRY does not exist, set ERROR to NoRegistry and stop.
  5. Add DEFINITION to REGISTRY
  6. Let MAP be REGISTRY's upgrade candidates map
  7. Run element upgrade algorithm with MAP and DEFINITION as arguments.

The definition construction algorithm creates an element definition and must be equivalent to running these steps:

Input
DOCUMENT, the document
TYPE, the custom element type of the element being registered
PROTOTYPE, the custom element prototype
NAME, a local name, optional
Output
DEFINITION, the element definition
ERROR, a variable that holds one of these values: None, InvalidType, InvalidName, or DuplicateDefinition
  1. Let ERROR be None
  2. Convert TYPE to lowercase
  3. If DOCUMENT is an HTML document, convert NAME to lowercase
  4. If TYPE is an invalid custom element type, set ERROR to InvalidType and stop.
  5. Let NAMESPACE be HTML Namespace
  6. If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG Namespace
  7. If there already exists a definition with the same TYPE, set ERROR to DuplicateDefinition and stop.
  8. If NAME was provided and is not null:
    1. Let BASE be the element interface for NAME and NAMESPACE
    2. If BASE does not exist or is an interface for a custom element, set ERROR to InvalidName and stop.
  9. Otherwise:
    1. If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
    2. Let NAME be TYPE
  10. Let LIFECYCLE be lifecycle callbacks
  11. Transfer callback named created to LIFECYCLE from property named createdCallback on PROTOTYPE
  12. Transfer callback named enteredView to LIFECYCLE from property named enteredViewCallback on PROTOTYPE
  13. Transfer callback named leftView to LIFECYCLE from property named leftViewCallback on PROTOTYPE
  14. Transfer callback named attributeChanged to LIFECYCLE from property named attributeChangedCallback on PROTOTYPE
  15. Let DEFINITION be an element definition with custom element type set to TYPE, local name to NAME, namespace to NAMESPACE, custom element prototype to PROTOTYPE, and lifecycle callbacks to LIFECYCLE.

The element upgrade algorithm upgrades unresolved elements whose definition is now registered and must be equivalent to running these steps:

Input
Let MAP, an upgrade candidates map
DEFINITION, element definition
Output
None
  1. Let TYPE be the custom element type in DEFINITION
  2. Let CANDIDATES be the sorted element queue for TYPE and NAMESPACE in MAP
  3. For each item ELEMENT in CANDIDATES:
    1. Enqueue callback created for ELEMENT
  4. Set CANDIDATES to empty sorted element queue.

7.1 Extensions to Document Interface

The register method of the Document interface provides a way to register a custom element and returns its custom element constructor.


partial interface Document {
    Function register(DOMString type, optional ElementRegistrationOptions options);
};

dictionary ElementRegistrationOptions {
     object? prototype = null;
     DOMString? extends = null;
};

When called, the register method must run these steps:

Input
DOCUMENT, method's context object, a document
TYPE, the custom element type of the element being registered
PROTOTYPE, the custom element prototype, optional
EXTENDS, the local name of an HTML or SVG element that is being extended, optional
Output
CONSTRUCTOR, the custom element constructor
  1. If PROTOTYPE is null, let PROTOTYPE be the result of invoking Object.create with HTMLElement's interface prototype object as only argument
  2. Let NAME be EXTENDS
  3. Let ERROR be the result of running the element registration algorithm with DOCUMENT, TYPE, PROTOTYPE, and NAME as arguments
  4. If ERROR is InvalidType, throw a SyntaxError and stop.
  5. If ERROR is not None, throw a NotSupportedError and stop.
  6. Return result of running custom element constructor generation algorithm with DOCUMENT and PROTOTYPE as arguments.

ElementRegistrationOptions is an abstraction that enables using function objects and ES6 classes as the second argument of document.register method.

In order to register a custom element with a prototype, other than HTMLElement or SVGElement, the caller of document.register has to first build a proper prototype object that inherits from HTMLElement. Here's a simple example of how one could do this:


document.register('x-foo', {
    prototype: Object.create(HTMLParagraphElement.prototype, {
        firstMember: {
            get: function() { return "foo"; },
            enumerable: true,
            configurable: true
        },
        // specify more members for your prototype.
        // ...
    })
});

7.2 Unresolved Element Pseudoclass

The :unresolved pseudoclass must match all custom elements whose created callback has not yet been invoked.

The :unresolved pseudoclass could be used to mitigate the Flash of Unstyled Content (FOUC) issues with custom elements.

8 Instantiating Custom Elements

The custom element type is given to a custom element at the time of its instantation in one of the two ways:

  1. As the local name of the custom element. These types of custom element types are called custom tags.
  2. As the value of the is attribute of the custom element. custom element types given this way are called type extensions.

After a custom element is instantiated, changing the value of the is attribute must not affect this element's custom element type.

If both types of custom element types are provided at the time of element's instantiation, the custom tag must win over the type extension.

All custom elements must be constructable with a function object, called custom element constructor. This constructor must be created with the custom element constructor generation algorithm, which must be equivalent to running these steps:

Input
PROTOTYPE, the custom element prototype.
DOCUMENT, the owner document for new custom element
Output
CONSTRUCTOR, the custom element constructor
  1. If PROTOTYPE is already an interface prototype object for any interface object or PROTOTYPE has a non-configurable property named constructor, throw a NotSupportedError and stop.
  2. Let DEFINITION be an element definition that has PROTOTYPE as custom element prototype
  3. Let REGISTRY be the DOCUMENT's registry
  4. Let CONSTRUCTOR be the interface object whose interface prototype object is PROTOTYPE and when called as a constructor, executes these steps:
    1. Let ELEMENT be the context object
    2. Let TYPE be the custom element type in DEFINITION
    3. Let NAME be the local name in DEFINITION
    4. Let NAMESPACE be the namespace in DEFINITION
    5. Set ELEMENT's local name to NAME, namespace to the NAMESPACE, and node document to DOCUMENT
    6. If TYPE is not the same as NAME, set the value of ELEMENT's is attribute to TYPE
    7. Enqueue created callback for ELEMENT
    8. Return ELEMENT.

8.1 Extensions to Document Interface

To allow creating both custom tag and type extension-style custom elements, the createElement or createElementNS methods have an optional typeExtension argument:


partial interface Document {
    Element createElement(DOMString localName, optional DOMString typeExtension);
    Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional DOMString typeExtension);
};

Instead of step 3 in createElement and step 9 in createElementNS (the steps that determine element interface, both methods must run the following steps:

  1. Let TYPE be typeExtension, or localName if typeExtension is not present
  2. If an element definition with matching localName, namespace, and TYPE is not registered with token's document, set TYPE to localName
  3. Let interface be the element interface for TYPE as local name and namespace (HTML Namespace for createElement)

Additionally, both createElement or createElementNS methods must run the following steps just before returning the result:

  1. If TYPE is not the same as localName, set the value of ELEMENT's is attribute to TYPE
  2. Enqueue created callback for ELEMENT

9 Parsing Custom Elements

To enable instantiating custom elements during tree construction, a conforming UA must run enqueue created callback for any custom element when popping it from the stack of open elements.

Enqueuing created callbacks when popping an element from the stack of open elements has the effect of ordering the callbacks in such a way that the childrens' callbacks are invoked prior to their parents' callbacks, and the siblings' callbacks are invoked in document order. Keeping unresolved elements in a sorted element queue has the same effect on the order of element upgrade.

This modification to tree construction has the consequence of custom elements being created when parsing HTML documents or fragments.

10 Appendix A: All Algorithms in One Diagram

To help navigate through various parts of the spec and their interactions, here is a diagram that attempts to put all algorithms actions that trigger them in one space. Click on each box to go to the corresponding algorithm or action.

Fig. All algorithms in one diagram

Acknowledgements

David Hyatt developed XBL 1.0, and Ian Hickson co-wrote XBL 2.0. These documents provided tremendous insight into the problem of behavior attachment and greatly influenced this specification.

Alex Russell and his considerable forethought triggered a new wave of enthusiasm around the subject of behavior attachment and how it can be applied practically on the Web.

Dominic Cooney, Hajime Morrita, and Roland Steiner worked tirelessly to scope the problem within the confines of the Web platform and provided a solid foundation for this document.

The editor would also like to thank Alex Komoroske, Anne van Kesteren, Boris Zbarsky, Daniel Buchner, Edward O'Connor, Erik Arvidsson, Elliott Sprehn, Hayato Ito, Jonas Sicking, Olli Pettay, Rafael Weinstein, Scott Miles, Simon Pieters, Steve Orvell, Tab Atkins, and William Chen for their comments and contributions to this specification.

See a problem? Select text and or view bugs filed.