This is a loader script for web components that were written down in HTML, as they should be! The Custom Element LOader, or Celo, listens for changes in DOM and when it detects a custom element being added, it fetches its markup and inserts it in the document.
Celo will autoload web components for you if your follow the rules:
- Import celo either as an ES6 module or a simple script.
- Your components should be in the folder "/components" (or any single folder set in the configuration).
- Your components' names must be lowercase and match the tags intended. So the "<my-example>" component will be loaded from the file "my-example.html".
Celo uses a MutationObserver to listen for any element inserted in the DOM that carries a hyphen (ie. a custom element). Upon detection, if it hasn't done so already, it fetches the markup and add its code to a hidden <div> with an id of "_celo" (also configurable).
Celo has no dependencies, but the non-minified version assumes ES6.
Celo comes in two flavours: you can import it as an ES6 Module or as a simple script. In either way, Celo is an iife. It will execute automatically, won't pollute your global namespace and though it returns the Mutation Observer, you most likely don't need to keep a reference to it.
The difference is that importing a script will block the HTML parser, while ES6 Modules are deferred by default, allowing your page to show the appearance quickly and then (quite quickly) load the interactivity. That is a great idea.
But Celo is the kind of thing you'd want to block the parser! As an ES6 Module, Celo will only be fired after the DOM is parsed, so it'll loop through all elements in your page and reinsert custom elements to be reparsed. Yikes! That means your page will blink without the components, before inserting them half a second later.
Importing it directly as a script would do the opposite, loading Celo and the components as they are needed, so your page is loaded in one go. The delay is usually minor, and there's no blink. This is currently the preferred method for loading Celo (even is modules do usually rock!).
<script type="module">
import '/celo.mjs'
</script>
In case you want a fallback, you can run the minified ES5 version with a nomodule attribute:
<script nomodule src="/celo.min.js"></script>
<script src="/celo.js"></script>
or
<script src="/celo.min.js"></script>
The script version is transpiled to work under ES5.
Methods of loading celo have changed slightly in recent version. Make sure to check the set up if you run into any troubles.
You don't need to configure anything - the defaults are most likely fine.
But if you want to, you can set the path of the folder your components are in, and the id of the <div> that will contain the templates. You do that by creating a global variable named "celoConfig" that can contain either or both of these properties: "componentsPath" and "containerId". The example below shows a simple way to configure Celo, but be sure to do it in a separate <script> tag and use "var" instead or let or const.
<script>var celoConfig = { containerId: "_customId" }</script>
Each web component should be its own HTML file. If you want multiple components in a single HTML, make sure it's one main component and its subcomponents. Basically a subcomponent is:
- dynamically added by the main component;
- not suitable to be added as a standalone component; and
- not intended to be reused by other main components.
If you add the subcomponent tag directly to your HTML, Celo will try to load it from a file matching its tagName, resulting in a non-fatal error.
Any top level <script> tags in your component's HTML file will be executed right away. But <script> tags inside <template> tags are (typically) not. That's because web components (typically) use the cloneNode() function, which flags the scripts as "already executed". Please note that:
- You don't need to include <script> tags inside the shadowRoot.;
- Even if you do include it in the shadowRoot, the script won't be scoped;
- And if you want to do it for some reason, that's an issue for to fix in the web component itself. Celo's recreateScripts() function could give you a tip on how to make the script self-executable, though.
- It doesn't make your app descriptive, reactive, responsive or progressive. It just allows you to load web components and lets you do your other chores whichever way you see fit.
- It doesn't cache your components for other visits. Try setting that up with service workers.
- It doesn't polyfill your components.
Below you will find how a "simple-example.html" file could look like (it's not the "right" way to do it, but it's a way that works). Basically, you've got two top level tags: a <template> and a <script>. All the markup goes into the <template>, including any <style>, and it's best to avoid adding extra <script> tags inside <template>.
<template id="tpl-simple-example">
<div>
<p>This is a demo web component.</p>
</div>
<style>
p{
padding: 5px 10px;
background-color: antiquewhite;
}
</style>
</template>
<script>
class SimpleExample extends HTMLElement{
constructor(){
super()
const el = document.querySelector("#tpl-simple-example")
.content.cloneNode(true)
this.attachShadow({mode:'open'}).appendChild( el )
}
}
customElements.define( 'simple-example',SimpleExample )
</script>