W3C home > Mailing lists > Public > public-webapps@w3.org > January to March 2013

Re: [webcomponents] calling JS on custom element construction

From: Scott Miles <sjmiles@google.com>
Date: Wed, 20 Mar 2013 10:48:02 -0700
Message-ID: <CAHbmOLbZTmQrDMpA_thyxqGN-F3Va9X0mjvjM0inkr7SPiM8mQ@mail.gmail.com>
To: Mike Kamermans <nihongo@gmail.com>
Cc: public-webapps <public-webapps@w3.org>
Sorry for the extra email, but I realize we didn't discuss 'constructor'.

Most user-agents today cannot construct an HTML element via a vanilla
constructor. For example,

new HTMLDivElement()
>> TypeError: Illegal constructor

The problem is that element construction code typically does 'under the
hood' bindings. There are efforts afoot to bring browser internals closer
to JavaScript, but we are not there yet.

For this reason, the 'custom elements' work in general has real problems
with constructors. Ideally we want to be able to register a vanilla
JavaScript class with a tag name and be done with it, but it cannot be so.

In the meantime, the custom elements systems all _produce_ a constructor
for you, and the best you can do is define the prototype and do your
initialization tasks in special callbacks (aka _readyCallback_ as in my
previous email).

For example, you will see usage of document.register like this:

  XFoo = document.register("x-foo", {prototype: XFooPrototype});

Notice that the XFoo constructor is emitted by register, you don't get to
supply one.

Similarly, when you supply constructor="MyCustomElement" attribute to
<element>, you are asking the system to emit the generated constructor into
a variable named "MyCustomElement".

There is more to talk about on this subject but I feel like I've been
long-winded already. Follow ups appreciated.

P.S. XFooPrototype shown above must extend HTMLElement.prototype (or a
descendent).

P.P.S. You can get fancy and do stuff like this, but you have to be careful

          // potential foot-gun, will not actually be our constructor

XFoo = function() {
 this.textContent = "I'm an XFoo!";

};
XFoo.prototype = Object.create(HTMLElement.prototype);
XFoo.prototype.readyCallback = XFoo; // tricky! pretend we are using our
ctor
// almost what we want except we have to capture new XFoo on the
left-hand-side
XFoo = document.register("x-foo", XFoo);




On Wed, Mar 20, 2013 at 10:35 AM, Scott Miles <sjmiles@google.com> wrote:

> The answer depends a bit on the particular implementation of
> HTMLElementElement (aka <element>) that you are using. The spec is behind
> the various discussions on this topic, so the implementations vary.
>
> Our main polyfill <https://github.com/toolkitchen/CustomElements> for HTMLElementElement
> adds a method called *register* to HTMLElementElement that allows you to
> specify a custom prototype for your custom element. If you add a *
> readyCallback* method on that prototype, it will be called when your
> custom element is instanced.
>
> For example,
>
>   <element name="x-foo">
>     <script>
>       this.register({
>         prototype: {
>           readyCallback: function() {
>             this.textContent = 'Hello World';
>           }
>         }
>       });
>     </script>
>   </element>
>
> Note that in this version of the polyfill <template> is not instanced for
> you at all, so you in fact need to do that yourself in your readyCallback.
> Specifically,
>
>   <element name="x-foo">
>     <template>
>       Hello World
>     </template>
>     <script>
>       var template = this.querySelector("template");
>       this.register({
>         prototype: {
>           readyCallback: function() {
>             // YMMV depending on your platform's interpretation of
> <template>
>             this.innerHTML = template.innerHTML;
>           }
>         }
>       });
>     </script>
>   </element>
>
> If you want to be free of all such worries, you can try the higher level
> code under the toolkit <https://github.com/toolkitchen/toolkit>repository,
> but it's even more bleeding edge.
>
> HTH,
> Scott
>
>
> On Wed, Mar 20, 2013 at 9:46 AM, Mike Kamermans <nihongo@gmail.com> wrote:
>
>> Hey all,
>>
>> still playing with web components, I was wondering if there's a way to
>> make a custom element trigger JS whenever an element is built either
>> through JS calls or when used in the DOM. From the spec, combined with
>> the toolkitchen polyfills, I can see that the element's <script> block
>> runs once, when the element gets defined, so I figured I could use an
>> explicit constructor instead and make things work that way:
>>
>>
>> var MyCustomElement = function() { console.log("element built"); }
>>
>> <element name="my-custom-element" constructor="MyCustomElement">
>>   <template>
>>       <content></content>
>>   </template>
>> </element>
>>
>> but this does not appear to call the MyCustomElement constructor when
>> the element is built through the DOM by virtue of just "being used on
>> a page", and when called on the console with "new MyCustomElement();"
>> I get the error "TypeError: Object #<HTMLDivElement> has no method
>> 'instantiate'"... If I use "MyCustomElement.prototype = new
>> HTMLDivElement()" to try to set up a sensible prototype chain, I just
>> get the error "Uncaught TypeError: Illegal constructor"./
>>
>> What's the recommended way to set up a custom element that runs some
>> JS or calls a JS function any time such an element is built for use on
>> the page?
>>
>> - Mike "Pomax" Kamermans
>>
>>
>
Received on Wednesday, 20 March 2013 17:48:34 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:58 GMT