Custom Elements v1 - "Hello World" demo
The code for Custom Elements v1 vs JS frameworks blog post by Ferdy Christant
The demo uses a custom element called <hello-world>
It will have a greeting message that says "Hello ”. Yet you can talk to the API to change the name property, which is then automatically updated in the DOM and on-screen. In addition, you can use the form as an additional way to change the name property. These two simple features test important features of component-based development: DOM synchronization and component-level event handlers.
The custom element also has a log panel. We’re outputting diagnostic info on what is happening in the life cycle so that we can easily check what is going on across devices, which may not always have a developer console.
The demo should contain 5 elements on screen:
Instance 1: basic SSR
The first instance of our custom element is entirely server-side rendered (SSR). It will initially say "Hello, world” yet if it is successfully upgraded, it will say "Hello, Upgraded world!”. Once upgraded, we’re not interacting with the API at all.Instance 2: SSR + change attribute
The second instance is also SSR, yet this time after upgrading it, we’re going to get a handle to it in JS and then change the name attribute, to see if it reflects in the DOM and on screen.Instance 3: SSR nested
Also SSR, yet this one is nested. So it’s a hello-world inside a hello-world, to test what that means for the scope.Instance 4: dynamic insert
This instance of the custom element is dynamically inserted into the DOM. Using just a string literal for the markup, but you could also have used Templates, Handlebars, or whatever. (See line ~248 insrc/js/main.js
)Log panel (in black)
: Besides having a diagnostic log per instance, there’s also a global on-screen log to check what is going on regarding global events, polyfill loading, or any errors we may get.
Chrome (56)
: 100%, native supportFirefox (51)
: 100%, using polyfillEdge (14)
: 100%, using polyfillOpera (42)
: 100%, native supportChrome for Android (55)
: 100%, native supportMobile Safari 10 (iOS10.2)
: 100%, using polyfill (tested both on iPhone and iPad)Safari 10 on Mac
: 100%, using polyfill
Reasonable native support that is soon to be improved (Safari’s latest version will deliver native support and is around the corner, Firefox and Edge are working on it)
npm i
or yarn install
The index.html
is hooked up and ready to go.
open index.html
Alternatively use the gulp script to do real SSR on a server.
See gulpfile.js
or package.json
"scripts"
entry.
npm run watch
(orgulp watch
)npm start
(orgulp scripts
)
scripts
is used to compile /src/js
files to /js
and enable live reload on change.
watch
is used to watch .js
files in src/js
for changes and trigger the scripts
script to compile and reload (on any such change).
The main.js
uses custom-elements.min.js
polyfill (see /js/poly/
) for polyfilling support for Custom Elements if not natively available (ie. if window.customElements
is available).
Get the latest @webcomponents/custom-elements f.ex via one of:
npm i -S webcomponents/custom-elements
(latest from github)npm i -S @webcomponents/custom-elements
(latest published on npm)
const supportsCustomElementsV1 = 'customElements' in window;
if (!supportsCustomElementsV1) {
// no native support, load polyfill
logScreen('Native support not available, loading polyfil...');
loadScript('js/poly/custom-elements.min.js',function() {
logScreen('Polyfill loaded.');
upgrade();
});
} else {
// native support, carry on
logScreen('Native support detected.');
upgrade();
}
Babel transpilation config
To get transpilation from ES6 to ES5 with Babel, we need to add some babel transform plugins in .babelrc
(or similar configuration).
{
"presets": [
"es2015"
],
"plugins": [
"transform-custom-element-classes",
"transform-es2015-classes"
]
}
However, legacy browsers like IE10 and IE11 may pose additional problems. The problem is not in the polyfill, it is in the transpilation. It may require additional polyfills (such as es6-shim) or additional Babel plugins. It’s a can of worms...
Source maps
To make transpilation bearable, I included source maps as part of the build process.
Custom Element definition
You define a Custom Element (v1) as described by creating a class
that extends HTMLElement
class HelloWorld extends HTMLElement {
constructor(element) {
// Always call super first in constructor
super();
// ...
}
// ... more methods
}
Dynamic custom element instantiation
You can then instantiate it by using new
which calls the class constructor to create the element Object
. You can then operate on it and eventually insert it into the DOM, here by using appendChild
.
let domStr = `
<p>Hello, <span class="name">World!</span></p>
<form class="frm" action="" method="post" accept-charset="UTF-8">
<input type="text" class="nameField" value="" />
<input type="submit" value="submit" />
</form>
<ul class="log">
<li>DOM init</li>
</ul>`;
let hw2 = new HelloWorld();
hw2.innerHTML = domStr;
hw2.setAttribute('id','hw3');
hw2.setAttribute('name','DOM created world!');
document.querySelector("#instances").appendChild(hw2);
// change a property after DOM creation to see if it reflects.
hw2.name = "DOM created world (JS)!";
Additionally, /js/poly/
contains core.min.js
, the standard JS library which:
"Includes polyfills for ECMAScript 5, ECMAScript 6: promises, symbols, collections, iterators, typed arrays, ECMAScript 7+ proposals, setImmediate, etc."
core
can thus be used to polyfill all the recent JS advances for older browsers (since ~2014).
MIT