Introduction
Web components are analogous to self-contained controls or components as you’ll have seen in Windows Forms, WPF etc. They allow developers to package up style, scripts and HTML into a single file which also is used to create a custom element, i.e. not part of the standard HTML.
Sadly, still not yet available in all browsers (although I believe Polyfills exist for the main ones). Hence, for this post you’ll either need a polyfill or the latest version of Google Chrome (which I’m using to test this with).
What does it look like using web components?
To start with we create an HTML file for our web component, we then define the template for the component – this is basically the combination of style and the control’s HTML. Finally we write scripts to effect the shadow DOM by interacting with our web component.
In this post we’re going to create a simple little web component which will flash it’s text. Before we look at the component code let’s see the component in use within the following index.html file
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Web Component Test</title> <link rel="import" href="flashtext.html"> </head> <body> <flash-text data-text="Hello World"></flash-text> </body> </html>
The key points here are that we include the web component using the line
<link rel="import" href="flashtext.html">
and then we use the custom element flash-text with it’s custom attributes and we use the component like this
<flash-text data-text="Hello World"></flash-text>
It’s important to note the closing element, a self closing element is not correctly handled (at least at this time using Google Chrome) and whilst the first usage of a web component might be displayed, if we had several flashing text components in the index.html it’s possible only one would be displayed and no obvious error or reason for the other components not displaying.
Creating our web component
We already decided our component will be stored in flashtext.html (as that’s what we linked to in the previous section).
To start off with, create the following
<template id="flashTextComponent"> </template> <script> </script>
We’ve created a new template with the id flashTextComponent. This id will be used within our script, it’s not what the pages using the component uses. To create a new custom element by adding the following to the script
var flashText = document.registerElement('flash-text', { });
But we’re getting a little ahead of ourselves. Let’s instead create some styling and the HTML for our component. Within the template element, place the following
<style> .flashText { float: left; width: 152px; background-color: red; margin-bottom: 20px; } .flashText > .text { color: #fff; font-size: 15px; width: 150px; text-align: center; } </style> <div class="flashText"> <div class="text"></div> </div>
The style section simply defines the CSS for both the flashText div and the text div within it. The div elements create our layout template. Obviously if you created something like a menu component, the HTML for this would go here with custom attributes which we’ll define next, mapping to the template HTML.
Next up we need to create the code and custom attributes to map the data to our template. Before we do this let’s make sure the browser supports web components by writing
var template = document.createElement('template'); if('content' in template) { // supports web components } else { // does not support web components }
If no content exists on the template, Google Chrome will report the error in the dev tools stating content is null (or similar wording).
Within the // supports web components section place the following
var ownerDocument = document.currentScript.ownerDocument; var component = ownerDocument.querySelector('#flashTextComponent'); var templatePrototype = Object.create(HTMLElement.prototype); templatePrototype.createdCallback = function () { var root = this.createShadowRoot(); root.appendChild(document.importNode(component.content, true)); var name = root.querySelector('.text'); name.textContent = this.getAttribute('data-text'); setInterval(function(){ name.style.visibility = (name.style.visibility == 'hidden' ? '' : 'hidden'); }, 1000); }; var flashText = document.registerElement('flash-text', { prototype: templatePrototype });
Let’s look at what we’ve done here. First we get at the ownerDocument and then locate our template via it’s id flashTextComponent. Now were going to create an HTMLElement prototype which will (in essence) replace our usage of the web component. When the HTMLElement is created we interact with the shadow DOM placing our component HTML into it and then interacting with parts of the template HTML, i.e. in this case placing data from the data-text custom attribute, into the text content of the div text.
As we want this text to flash we implement the script for this and attached to the visibility style of the text.
Finally, as mentioned previously, we register our custom element and “map” it to the previously created prototype.
Using in ASP.NET
ASP.NET can handle static pages easily enough, we just need to add the following to the RouteConfig
routes.IgnoreRoute("{filename}.html");
Now, inside _Layout.cshtml put in the head section
and within the Index.cshtml (or wherever you want it) place your custom elements, i.e.
<flash-text data-text="Hello World"></flash-text>
References
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
https://developers.google.com/web/fundamentals/web-components/customelements
https://www.webcomponents.org/
https://stackoverflow.com/questions/45418556/whats-the-reason-behind-not-allowing-self-closing-tag-in-custom-element-in-spec
Code for this post
https://github.com/putridparrot/blog-projects/tree/master/webcomponent