Re: HTMLElement.register--giving components tag names

On Tue, Aug 30, 2011 at 10:33 PM, Dominic Cooney <> wrote:
> "Components" (see
> for examples of
> what I mean) need to present an API to script. For example, a
> "contacts" component might want to expose a refresh() method that
> pulls new contacts from the server. Components also need to hook up
> internal behavior implemented in script; a split pane might need to
> hook up a mouse listener on its splitter before it can really behave
> like a split pane.
> In script widget libraries today (YUI, Closure, etc.) this is easy
> when the widget has been created from script: when the author calls
> the constructor/factory method/whatever the widget can create DOM,
> hook up event listeners, set up the prototype chain however it wants,
> etc.
> However things get really murky when authors start to use markup,
> because then the parser is creating elements that are at best only
> minimally functional until they're "enhanced." So today widget
> implementations need to handle enhancing an existing element;
> frameworks need to detect these unenhanced elements and invoke
> something to enhance them; and authors needs to debug this unholy mess
> when an unenhanced element escapes and their page breaks.
> Wouldn't it be nice if the browser made this problem go away?
> I think HTML itself shows us how life could be better: Consider the
> HTML video element. It looks like a "subtype" of HTML element, with
> all of the methods and attributes a HTML element has (via the
> prototype chain), and adds methods and attributes specific for doing
> video. When you do document.createElement('video') you get back an
> object that is ready to use. There's no step to "enhance" it. When you
> do p.innerHTML = "<video src= controls=true>", you get a video
> player. There's no flash of unstyled content. If you clone it and
> insert the copy into the DOM again, all of the playback controls are
> still wired up and responsive.
> I'm proposing we add something that lets script extend the set of tag
> names, so there is less of a bright line between elements defined in
> the HTML spec and elements defined in script. Something like:
> HTMLElement.register('x-contacts', ContactPicker);
> The first argument is an element name. The second is a constructor
> function, whose prototype property points to an object which
> introduces the API for contacts (eg a refresh method) and is wired up
> to HTMLElement.prototype.
> When the parser encounters <x-contacts/>, it creates an object by
> calling the ContactPicker function as a constructor. The constructor
> can wire up any internal state it wants, like creating DOM and
> attaching event listeners. When a script later retrieves the element,
> say with document.querySelector('x-contacts'), the object created
> earlier with ContactPicker is what it gets back. Since the constructor
> is run as the parser is creating the element, there's no time when the
> unenhanced element is available to script.
> My proposal pretty much ends here: The component itself is just
> implemented with script, DOM and CSS. There are no special hooks for
> participating in layout (just use DOM and CSS) or form submission
> (just create form controls using DOM) or security (same origin policy
> still applies) or networking (just use WebSockets or XHR) or anything
> else. There is no magic, other than teaching the browser about the new
> tag name.
> I'm suggesting that all registered tag names have to start with x-.
> This is to give component authors a place to build without worrying
> about conflicting with future HTML specs; the HTML spec won't define
> elements with names starting with x-. (Component authors may chose x-
> names that conflict with each other, though.) People working on the
> HTML spec can study what kinds of components are popular in the wild
> and consolidate the best ideas into new HTML elements. Page authors
> can decide if and when to move their pages from their bespoke x-
> components to the HTML alternatives, which should be better.
> Pages that have to work in older browsers can use fallback content.
> (This is exactly what the HTML spec advises authors to do with the
> video element, incidentally.) In newer browsers the component script
> can remove or hide the fallback content.
> There's the question of what to do when there is a call to
> HTMLElement.register('x-contacts', ) at some point after an x-contact
> element has been parsed. We have to do something that makes sense for
> async scripts. I think the solution is to replace the element, and the
> recent discussion on this list about renameNode is interesting and
> relevant.
> One practical issue with this proposal is that creating a subtype of
> HTMLElement in script (ie defining the ContactPicker function in the
> above example) is hard/impossible. A couple of the problems are:
> - Defining a subtypes in JavaScript is hard to begin with: You need to
> create a function, and maybe set its object prototype up; you need to
> create a prototype object and wire its object prototype up; you might
> add a constructor property to the new prototype object; and you need
> to remember to call your "base type" constructor to let it do any
> setup it requires.
> - Even if you do all this,, which you would use
> to initialize your "base" type, doesn't work in the browsers I tried:
> If HTMLElement even has a call method, it raises an exception if you
> try to use it.
> For now, I think we need something that (a) is implementable in
> browsers today and (b) is harmonized with where JavaScript is going in
> TC39 and where DOM bindings are going ala Web IDL.
> The root cause of most implementation difficulties I see (I mostly
> looked at WebKit) is that when a JavaScript wrapper object is created,
> the browser wants to allocate a special wrapper object. It can't know
> to do this in advance in the general subtyping case. So I suggest we
> limit component authors to an "init" method that can create child DOM
> and register event listeners. This is not unlike XBL's
> enteredDocument, YUI's renderUI/bindUI, etc.
> The browser can provide a factory that mints constructors that call
> this init method at the appropriate time, but meet the browser's
> idiosyncratic constraints of how wrapper object construction unfolds.
> Using it would look like this:
> function initContacts(element) {
> // Set up child DOM
> // Add event listeners
> }
> var ContactPicker = HTMLElement.extend({init: initContacts});
> The resulting function can be used with HTMLElement.register, or even
> called directly:
> HTMLElement.register('x-contacts', ContactPicker);
> document.body.appendChild(document.createElement('x-contacts')); // works
> document.body.appendChild(new ContactPicker()); // also works
> You will notice that this says nothing about how prototypes are wired
> up. It should. Maybe the argument to extend should have an optional
> second field, proto, that specifies the new methods/getters/setters
> that ContactPicker's prototype should introduce.
> This is not a general subtyping mechanism! It is only designed for
> setting up subtypes of HTMLElement for use with register. When
> ECMAScript and the DOM bindings are sufficiently aligned,
> HTMLElement.register can be opened up to accept constructors defined
> using ordinary ECMAScript mechanisms for subtyping DOM interfaces.
> Scripts can continue to use extend (it is pretty succinct) or
> constructors set up their own way.

I am maniacally excited about getting this built into DOM. Lots of
libraries attempt to implement this, and quite a few of them fail at
it in their own subtle, miserable ways.

Of course, this is just temporary stopgap measure until we have
classes: Then,
we can truly have an elegant way of doing this:

class ContactPicker extends HTMLElement {
   constructor() {
       super(); // please, pretty please make this implicit,
ECMAScript people!!!
  /* even more awesome stuff goes here */


> Lastly, I wanted to mention how this relates to the component model.
> This is one small part. It could be used with the other parts--a
> component constructor could set up its presentation aspects in shadow
> DOM, for example. It could also be used by itself. As I illustrated
> above it eliminates a lot of the difficulties of working with widgets
> today.
> Thanks for reading this far! These proposals aren't formal or
> detailed. I would love to get feedback as I try to nail down some
> specifics.

Received on Wednesday, 31 August 2011 20:20:32 UTC