In the last dispatch, we talked about the HTML elements that support Web components, template and slot
namely. In this dispatch, we will pick up on the JavaScript aspect of the Web component. Let us revisit the
scaffolding we had prepared last time –
customElements.define(‘term-life’,
class extends HTMLElement {
…
});
customElements.define(‘editable’,
class extends HTMLElement {
…
});
class is native to JavaScript, in particular ECMA standard 6 and onwards. Readers coming from a C#
background will quickly interpret that we are extending the behaviour of HTMLElement. Remember the
question we posed in the opening of our last dispatch; Is HTML a programming language? Here we have
class and then to add to it HTMLElement. Readers with a C# background will equally be confused by the
absence of a class name and we will also clarify that HTMLElement is an interface instead of a class. Many
HTML elements one uses in a document implement the interface HTMLElement. It belongs to the DOM
API. Now about the absence of a class name, if you are familiar with anonymous functions extend that idea
to JavaScript. Instead of a function read a class. But then in JavaScript, a class is, in a manner of speaking
a closure i.e., a function. Let us expand on the first customElement.
customElement is a property defined on the Window object for a browser. The property is of type
CustomElementRegistry, the interface responsible for maintaining the custom elements registered for an
HTML document. The define method on the interface allows one to map the element text in the example
above ‘term-life’ to the class that implements the interface HTMLElement. Like in Generics one could
separate additionally specify type constraints to guide the usage of this customElement. One can do that by
passing an additional optional parameter like this –
customElements.define(‘term-life’,
class extends HTMLElement {
…
},
{
extends:’p’
}
);
In the code above we offer a type constraint equivalent to ‘term-life’ where we instruct only HTML elements
which are of type p can use this new element markup and behaviour. To use this component, it will look like
<p is=”term-life”> … </p>
Let us circle back to the custom element. There is a pattern that is typically used to develop such custom
elements. The following code snippet outlines the idea –
customElements.define(‘term-life’,
class extends HTMLElement {
//TODO: Create a shadow element
//TODO: Create new elements or content using HTML DOM API
//TODO: Attach any event handler or function as needed by the component.
});
Shadow root, if that intrigues remember from our last dispatch, it is the new API interface introduced to
make Web component work. A shadow root is actually an HTML element that represents the first element
of Shadow DOM. The name shadow root stuck around like naming a variable foo. It represents the
beginning of shadow DOM. Please do not confuse it with virtual DOM. React framework made it very
popular. However, they are vastly different. If we summarize the difference in brief, it will be that the virtual
DOM is creating a whole new object whereas the shadow DOM is creating a fragment within an existing
DOM.
The shadow DOM is created by invoking the DOM API
const shadowRoot = this.attachShadow({mode:’open’});
this refers to the context of the class where this is invoked. The option passed to the attachShadow
indicates that the new fragment that gets attached to the original DOM is accessible to the containing
HTML page. This was one of the major shortcomings of Web components which was addressed so that
JavaScript outside the component could work with the DOM elements attached by the component. New
elements are added using DOM API like this –
const dynamicParagraph = document.createElement(‘p’);
shadowRoot.appendChild(dynamicParagraph);
Putting these together let us complete the ‘term-life’ component as follows –
customElements.define(‘term-life’,
class extends HTMLElement {
constructor() {
super();
const scaffolding = document.getElementById(‘customer-acquisition’);
const contentInScaffolding = scaffolding.textContent;
const scaffoldingStyle = document.createElement(‘style’);
scaffoldingStyle.textContent = `
border: solid 1px var(–bs-green);
overflow-y: scroll;
width:80%;
text-align: justify-all;
}`;
const shadowRoot = this.attachShadow({mode:’open’});
shadowRoot.appendChild(scaffoldingStyle);
shadowRoot.appendChild(contentInScaffolding.cloneNode(true));
}
});
customElements.define(‘editable’,
class extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode:’open’});
const controlStyle = document.createElement(‘style’);
//Code to define the style of the control
const textInput = document.createElement(‘input’);
textInput.value = this.textContent;
this.addEventListener(‘click’,() => {
textInput.focus();
textInput.setSelectionRange(0, textInput.value.length);
});
});
In the above Web components, we have displayed two styles one using the HTML template and slot
element, and another using DOM API with attached event listener. Powered with your creativity sky is the
limit for how you could put Web components to use. In case you are wondering about distributing this, they
can be deployed like any other HTML, CSS and Script file. The HTML page which imports the JavaScript
should have a resolvable URL. Larger scale projects could work on a folder structure and use a separate
CDN for such components than the one which consumes these components. With that, we leave you with a
note – Happy programming Web components.