ES6 Web Components Part 3 - Making an ES6 Component Class

One thing I didn't cover in my last post (#2 in the series) about all the pieces that bring Web Components together is actually using ES6! It's not a feature of Web Components, but its some seriously nice glue that lets you tie your Web Component structure together.

To be honest, I'm not using it to it's full capacity. In fact, I'm only using 2 or 3 features to make my life easier!

Classes #

There are some strong opinions NOT to use classes especially from one prominent person in the JS community. Even if Crockford had all the best intentions, I doubt he had Web Components in mind - because to me, Web Components are the perfect place to use Classes. Components are fairly OO by nature. I want to take an HTMLElement and extend it to my own custom component. One thing I didn't get into when talking about creating my own elements before was the methods that you get when extending HTMLElement. Now is as good a time as any to bring them up:


class MyCustomClass extends HTMLElement {
    // Fires when an instance was removed from the document.
    detachedCallback() {};

    // Fires when an attribute was added, removed, or updated.
    attributeChangedCallback(attr, oldVal, newVal) {};

    // Fires when an instance was inserted into the document.
    attachedCallback() {
        // Remember this? We're cloning the template tag and dropping it
        // onto our page in our newly created Shadow Root
        var template = this.owner.querySelector('template');
        var clone = document.importNode(template.content, true);
        this.root = this.createShadowRoot();
        this.root.appendChild(clone);
    };

    // Fires when an instance of the element is created.
    createdCallback() {};
}
if (document.createElement('my-customclass').constructor !== MyCustomClass) {
MyCustomClass.prototype.owner = (document._currentScript || document.currentScript).ownerDocument;
    document.registerElement('my-customclass', MyCustomClass);
}

So the above code are all the methods you get when you extend an HTMLElement AND what an empty ES6 Class might look like. The methods inline with the comments seem pretty self explanatory, but I will mention this...pay VERY close attention to "attachedCallback" vs "createdCallback". Yes, its obvious that one fires when the element is created vs added to the DOM, however - make sure you consider that difference especially when you create an instance of your element at runtime with Javascript. If you did a bunch of cool stuff with "attached", but then did document.createElement('my-customclass') to create your element using JS....it hasn't been added to the DOM yet! So whatever you have in "attachedCallback" hasn't been run yet and your component might not act like you expect!

And now the non-obvious stuff...that weird "if" statement I have outside of my component. Well, this is all to register your custom element with the browser.

First, we try to create the custom element. If this is the second time you've used the component, it stands to reason that it's all been registered before and we'd be writing over the original element! So, we create our custom element, and if it's constructor is our class (MyCustomClass), then it's already been created and we shouldn't so it again.

Next, I'm using a little known feature: document.currentScript.ownerDocument. Unfortunately, document.currentScript is not present in all browsers. Yet again, WebComponent.js is there to save our bacon. In this scenario, with a polyfill though, _currentScript appears with an underscore, so you have to handle the logic.

In any event, this "ownerDocument" tells us the "document" owned by our script. In this context, document is our component's local DOM! So, super useful, right? If you want to append any children, you do it to this ownerDocument. Create a ShadowDOM? Do it on this ownerDocument. Clone your