Re: HTMLElement.register--giving components tag names

On Tue, Aug 30, 2011 at 10:33 PM, Dominic Cooney <dominicc@chromium.org> wrote:
> "Components" (see
> http://wiki.whatwg.org/wiki/Component_Model_Use_Cases 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, HTMLElement.call(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});

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: http://wiki.ecmascript.org/doku.php?id=harmony: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 */
}

:DG<

Received on Wednesday, 31 August 2011 20:17:43 UTC