- From: Dimitri Glazkov <dglazkov@google.com>
- Date: Wed, 31 Aug 2011 13:17:07 -0700
- To: Dominic Cooney <dominicc@chromium.org>
- Cc: WebApps WG <public-webapps@w3.org>
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