Re: [webcomponents] Auto-creating shadow DOM for custom elements

On Dec 6, 2013, at 7:41 PM, Elliott Sprehn <esprehn@chromium.org> wrote:

> 
> On Thu, Dec 5, 2013 at 5:37 PM, Ryosuke Niwa <rniwa@apple.com> wrote:
> Hi,
> 
> Given that many important/natural use cases of custom elements involve shadow DOM,
> can we add a flag to auto-create shadow DOM for custom elements?
> 
> In particular, can we add "template" as the third argument to document.register so that
> when a custom element is "instantiated", the specified template is automatically closed
> and inserted into a shadow DOM of the custom element.
> 
> 
> Can you explain the advantage of this? It saves one line of code:
> 
> this.createShadowRoot().appendChild(document.importNode(template.contents));
> 
> I don't see how pushing that one line down into the browser is helping anyone.

It's not reasonable to require developers to write the above super tricky line of code. It involves at least four different concepts and is easy to get subtly wrong. 

For example, when I wrote my first component (after much reading of docs), I did this, which has at least two bugs[1]:

        shadow = this.createShadowRoot(this);
        shadow.appendChild(template.content.cloneNode(true));

One of these bugs was copied from the Web Components Primer even.

In practice, the code delta is going to be more than one line, if you want to avoid polluting the global namespace.

Here is my attempt at writing a trivial component with Custom Elements, Shadow DOM and Templates as they exist today. Note that this works in Chrome Canary as of a week ago with experimental web features turned on:
I sincerely tried to minimize verbosity as much as possible, without introducing gratuitous namespace pollution or sacrificing clarity.

Key lines:

    <!-- custom element definition -->
    <script>
    (function () {
        var prototype = Object.create(HTMLElement.prototype);
        var template = document.getElementById("rainbow-span-template");

        prototype.createdCallback = function() {
            var shadow = this.createShadowRoot(this);
            shadow.appendChild(template.content.cloneNode(true));
        }

        document.register("rainbow-span", {prototype: prototype});
    })();
    </script>


Here is how it would look with the proposed 'template' parameter to document.register:
This replaces 9 rather non-obvious non-blank lines of code with 1:

    <!-- custom element definition -->
    <script>
    document.register("rainbow-span", {template: document.getElementById("rainbow-span-template")});
    </script>


Now, obviously things will get more complicated with components that do need to run code on creation beyond the basics. But I believe there will be substantial complexity savings. To make this work properly for more complex cases, the createCallback would need to take


[1] (a)Forgot to var-declare shadow; (b) used cloneNode instead of importNode which would be wrong in case of a template from an import document.



> Web components are part of the extensible web concept where we provide a minimal subset of features necessary for opinionated frameworks to build things on top. Supporting <template> in document.register is easily done in script, so I believe it's better left to developers as they're better at building frameworks than we are.
> 
> In either case, that's something we can always add later so it shouldn't stand in the way of the current spec.

The spec as it is makes it gratuitously complicated to create a basic component. As my examples above demonstrate, this would be a significant improvement in developer ergonomics and reduction of learning curve for a modest increase in implementation/spec complexity.

I agree that it's important to provide the right building blocks for libraries. But that's not a reason to make things needlessly complicated if you try to code to the raw APIs. And the proposed template parameter is a good building block for libraries too!


Regards,
Maciej

Received on Saturday, 7 December 2013 06:45:17 UTC