Custom Elements

W3C First Public Working Draft 14 May 2013

This version
http://www.w3.org/TR/2013/WD-custom-elements-20130514/
Latest version
http://www.w3.org/TR/custom-elements/
Latest editor's draft
https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
Previous version
none
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/.

Publication as a First Public 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 published by the Web Applications Working Group as an Editor's Draft. If you wish to make comments regarding this document, please send them to public-webapps@w3.org (subscribe, archives). All feedback is welcome.

Publication as an Editor's 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 Terminology
  4. 4 Custom Element Lifecycle
  5. 5 Registering Custom Elements
    1. 5.1 Unresolved Element Pseudoclass
  6. 6 Instantiating Custom Elements
  7. 7 Serializing and Parsing Custom Elements in HTML
  8. 8 Declaring Custom Elements
  9. Acknowledgements

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.

Dependencies

This document relies on the following specifications:

Terminology

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

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

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

The element definition is an association of a custom element name, local name and a namespace with a custom element prototype, and lifecycle callbacks.

Custom Element Lifecycle

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

  1. Custom element is created before its custom element definition is associated with the document
  2. Definition of a custom element is associated with the document
  3. Custom element instance is created after its definition was associated with the document
  4. Custom element is inserted into a document
  5. Custom element is removed from the document
  6. Custom element is garbage-collected.

A custom element can optionally implement several callbacks that are invoked when the 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.

To retrieve a callback named name for an element, the user agent must run the following steps:

Input
NAME, name of the callback
ELEMENT, the element
Output
CALLBACK, the callback
  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, or null if there is no such callback.

Each unit of related similar-origin browsing contexts has an associated lifecycle callbacks queue, which is initially empty. Each item in the queue consists of the callback and the callback this value.

Also, each unit of related similar-origin contexts has a running lifecycle callbacks flag, which must initially be false. It is used to prevent reentrant invocation of the algorithm to invoke lifecycle callbacks.

To invoke lifecycle callbacks, 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 RUNNING be the running lifecycle callbacks flag
  2. If RUNNING is true, stop.
  3. Set RUNNING to true.
  4. Let INVOCATIONS be a copy of the ENVIRONMENT's lifecycle callbacks queue
  5. While INVOCATIONS is not empty:
    1. Empty ENVIRONMENT's lifecycle callbacks queue.
    2. For each INVOCATION in INVOCATIONS:
      1. Invoke callback in INVOCATION with the corresponding callback this value
    3. Let INVOCATIONS be a copy of the ENVIRONMENT's lifecycle callbacks queue
  6. Set RUNNING to false.

The following callbacks are recognized:

ready
invoked after custom element instance is created and its definition is associated with the document. The actual timing of this callback is defined further in this specification.
inserted
Unless specified otherwise, this callback must placed into the lifecycle callbacks queue with the callback this value of the custom element whenever custom element is inserted into a document.
removed
Unless specified otherwise, this callback must placed into the lifecycle callbacks queue with the callback this value of the custom element whenever custom element is removed from the document.

Registering Custom Elements

Element registration is a process of associating an element definition with a document. An element definition can only be registered with one document. Once registered, the custom element's interface must be the element interface for local name and namespace values of custom element name and the namespace in custom element definition, respectively.

Because element registration can occur at any time, a custom element could be created before it 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 element with a valid custom element name will have HTMLElement (or SVGElement) as element interface, rather than HTMLUnknownElement.

Each browsing context has an associated map of all instances of unresolved elements for a given pair of custom element name and namespace. This data structure is called the upgrade candidates map and is initially empty.

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

As an element definition is registered, all corresponding unresolved elements are upgraded using the element upgrade algorithm, which must be equivalent to running these steps:

Input
CONTEXT, a browsing context
DEFINITION, an element definition
Output
None
  1. Let TYPE be the custom element name in DEFINITION
  2. Let NAMESPACE be the namespace in DEFINITION
  3. Let PROTOTYPE be the custom element prototype in DEFINITION
  4. Let MAP be CONTEXT's upgrade candidates map
  5. Let CANDIDATES be the list of all unresolved elements for TYPE and NAMESPACE in MAP
  6. For each item ELEMENT in CANDIDATES:
    1. Set the value of the [[Prototype]] internal property of ELEMENT to PROTOTYPE
    2. Let READY be the result of retrieving a callback named ready.
    3. Place READY and callback this value of ELEMENT into the lifecycle callbacks queue.
    4. If ELEMENT is in a document:
      1. Let INSERTED be the result of retrieving a callback named inserted.
      2. Place INSERTED and callback this value of ELEMENT into the lifecycle callbacks queue.
  7. Set CANDIDATES to empty list

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 name, optional ElementRegistrationOptions options);
};

dictionary ElementRegistrationOptions {
     object? prototype = null;
};

When called, the register method must run these steps:

Input
ENVIRONMENT, the unit of related similar-origin browsing contexts
DOCUMENT, the context object of the method
NAME, the custom element name of the element being registered
PROTOTYPE, the custom element prototype, optional
Output
CONSTRUCTOR, the custom element constructor
  1. If NAME is an invalid custom element name, throw an InvalidCharacterError and stop.
  2. Let NAMESPACE be HTML Namespace
  3. Let TYPE be NAME
  4. If PROTOTYPE is null, let PROTOTYPE be the result of invoking Object.create with HTMLElement's interface prototype object as only argument
  5. Otherwise:
    1. If PROTOTYPE and DOCUMENT are from different browsing contexts, throw a NotSupportedError and stop.
    2. Let INTERFACE be PROTOTYPE's interface
    3. If INTERFACE inherits from SVGElement, set NAMESPACE to SVG Namespace
    4. Otherwise, if INTERFACE does not inherit from HTMLElement, throw a NamespaceError and stop.
    5. Let BASE be INTERFACE's inherited interface that is nearest to BASE in the prototype chain and that is an element interface
    6. If BASE is not HTMLElement or SVGElement, let NAME be the local name, associated with BASE element interface
  6. If DOCUMENT is an HTML document, convert both NAME and TYPE to lowercase
  7. If there already exists a definition with the same TYPE, throw a NotSupportedError and stop.
  8. Let LIFECYCLE be lifecycle callbacks
  9. Transfer callback named ready to LIFECYCLE from property named readyCallback on PROTOTYPE
  10. Transfer callback named inserted to LIFECYCLE from property named insertedCallback on PROTOTYPE
  11. Transfer callback named removed to LIFECYCLE from property named removedCallback on PROTOTYPE
  12. Let DEFINITION be the set of (TYPE, NAME, NAMESPACE, PROTOTYPE, LIFECYCLE)
  13. Register the DEFINITION with DOCUMENT
  14. Run element upgrade algorithm with DOCUMENT's browsing context and DEFINITION as arguments
  15. Invoke lifecycle callbacks with ENVIRONMENT as argument
  16. Return result of running custom element constructor generation algorithm with PROTOTYPE as argument.

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

The element upgrade and invoking lifecycle callbacks is intentionally decoupled to provide slightly more sensible outcomes of callbacks trying to access methods and properties of custom elements of the same kind. Also, because of when lifecycle callbacks are added to the lifecycle callbacks queue, the queue should be empty when document.register is invoked.

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.
        // ...
    })
});

Unresolved Element Pseudoclass

The :unresolved pseudoclass must match all unresolved elements.

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

Instantiating Custom Elements

The custom element name 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 names are called custom tags.
  2. As the value of the is attribute of the custom element. Custom element names 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 name.

If both types of custom element names 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.
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 CONSTRUCTOR be the interface object whose interface prototype object is PROTOTYPE and when called as a constructor, executes these steps:
    1. Let TYPE be the custom element name in DEFINITION
    2. Let NAME be the local name in DEFINITION
    3. Let NAMESPACE be the namespace in DEFINITION
    4. Let ELEMENT be the context object
    5. Set ELEMENT's local name to NAME, namespace to the NAMESPACE, and node document to the document with which DEFINITION is registered
    6. If TYPE is not the same as NAME, set the value of ELEMENT's is attribute to TYPE
    7. Run the element initialization algorithm with ELEMENT as argument
    8. Return ELEMENT.

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. Run element initialization algorithm with the newly created element

The element initialization algorithm must be equivalent to running these steps:

Input
ELEMENT, an element being initialized
Output
None
  1. Let DEFINITION be ELEMENT's definition
  2. Let READY be the result of retrieving a callback named ready
  3. If it is not null, invoke READY callback with callback this value of ELEMENT

Serializing and Parsing Custom Elements in HTML

To enable instantiating custom elements during tree construction, a conforming UA must run these steps when creating an element for a token:

Input
DOCUMENT, the document in which the element is being created
TOKEN, token tag name
NAMESPACE, token namespace
Output
ELEMENT, an element
  1. Let NAME be the tag name of the token
  2. Let NAMESPACE be the token's namespace
  3. Let TYPE be the value of is attribute for the token or NAME, if this attribute is not present
  4. If DOCUMENT is an HTML document, convert both NAME and TYPE to lowercase
  5. If an element definition with matching NAME, NAMESPACE, and TYPE is not registered with token's document, set TYPE to NAME
  6. Let ELEMENT be the result of creating a node implementing the interface appropriate for the element type corresponding to TYPE and NAMESPACE
  7. Let READY be the result of retrieving a callback named ready.
  8. Place READY and callback this value of ELEMENT at the front of the lifecycle callbacks queue.

This modification to creating an element for a token has the consequence of custom elements being created when parsing HTML documents or fragments.

In addition, just before the user agent is to perform a microtask checkpoint, the user agent must invoke lifecycle callbacks for the unit of related similar-origin browsing contexts to which the scripts' browsing context belongs.

The custom elements must be serialized like any HTML elements.

Declaring Custom Elements

Specify how custom elements are defined declaratively.

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, Steve Orvell, Tab Atkins, and William Chen for their comments and contributions to this specification.

This list is too short. There's a lot of work left to do. Please contribute by reviewing and filing bugs—and don't forget to ask the editor to add your name into this section.