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

On Dec 7, 2013, at 3:31 PM, Dominic Cooney <dominicc@google.com> wrote:

> 
> 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.
> 
> I assume you are referring to the Explainer (if not, could you please provide a link.) I am the editor of the Explainer. Please file a bug. In some areas the Explainer is out of date.

I meant the document titled "Introduction to Web Components". It's the document here: <http://w3c.github.io/webcomponents/explainer/>. At first I did not know what you meant by Explainer but I see now that the URL contains that.

> 
> I do not find either of those lines in the Explainer. But I could surmise that you are referring to using cloneNode instead of importNode. No doubt your bug will explain what you mean.

The specific lines are:

        this._root = this.createShadowRoot();
        this._root.appendChild(template.content.cloneNode());

And yes, the issue is that cloneNode() is not a best practice since in general the template might be imported. (I don't think we should recommend to authors that they pick cloneNode vs importNode on a case-by-case basis). 

I'm not sure offhand how to file a bug against this document but if you point me to the right bug tracker and component I'll be glad to do so. It

Another bug I noticed: the callback is named readyCallback instead of createdCallback.

Note: it appears that everywhere a shadow root is created in the Introduction, it's immediately followed by cloning a template into its contents. The intro certainly seems to assume they will be used together, despite the objections to making that path more convenient. And similar boilerplate appears over and over.

As an alternate suggestion, and one that might dodge the subclassing issues, perhaps createShadowRoot could take an optional template argument and clone it automatically. Then this:

        this._root = this.createShadowRoot();
        this._root.appendChild(template.content.cloneNode());

Could turn into this:

        this._root = this.createShadowRoot(template);

Which is quite a bit simpler, and involves fewer basic contents.


>  
> 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, 14 December 2013 09:02:37 UTC