Provides the ability to use custom-elements as if they were natively supported by the browser.
Necessary modules are lazily loaded upon first occurrence of the element in the DOM.
Mainly a proof of concept for environments where the smaller initial footprint of JavaScript resources is more important than fewer requests.
Bundlers like Webpack are explicitly not supported as the use case for lazy-loading is contradictory to bundling anything beforehand.
However, browser-native Web-Bundles (see: https://web.dev/web-bundles/) should work.
- ES6 compliance
MutationObserver
supportimport()
-Operator support
To get startet, each custom-elements module must provide a default export in a js-file located in /js.
Eg.: domain.tld/js/my-element.js
Also CustomElementsLazyLoader
must be provided in a way the browser can handle, which may be a module also.
domain.tld
│ index.html
└───js
│ │ main.js
│ │ custom-elements-lazy-loader.mjs
│ │ my-h1-element.js
│ │ my-h2-element.js
│ │ my-section-element.js
│ │ my-article-element.js
<!-- my-h1-element.js -->
export default class MyHeadingElement extends HTMLHeadingElement
{
//...
}
<!-- index.html ... -->
<body>
<h1 is="my-h1">Hello i'm a custom heading</h1>
<my-section>I'm a autonomous custom-element.</my-section>
</body>
<script type="module">
import CustomElementsLazyLoader from "/js/custom-elements-lazy-loader.mjs";
new CustomElementsLazyLoader().observe(document.body);
const customHeading = document.createElement('h2');
customHeading.setAttribute('is', 'my-h2');
document.body.append(customHeading, document.createElement('my-article'));
customElements.whenDefined('my-section')
.then(( name ) => { console.log(name); });
</script>
If everything is set up correctly, my-h1
, my-h2
, my-section
and my-article
get defined automatically,
without the need to do this beforehand.
You may use CustomElementRegisty::whenDefined() to do something when an Element is defined.
filter
:CustomElementsLazyLoader({ filter: (name: String):boolean })
Customize filtering. Say you'd want to load only element with a specific prefix:
Example:(name) => name.startsWith('prefix-')
urlResolver
:CustomElementsLazyLoader({ urlResolver: function(name: String): URL|String })
Customize URLs. Load *-element.mjs from /js/modules:
Example:(name) => new URL(`/js/modules/${name}-element.js`, window.location)
loader
:CustomElementsLazyLoader({ loader: (url: URL):CustomElementConstructor })
Customize the actual loading (rarely needed).
Example:(url) => return import(url).default
See JS / TS Docs und Unit-Test for further details.
element
:.observe(element)
narrows the scope of observation to the element and its subtree.subtree
:.observe(element, { subtree: false })
limits the observation to the element itself.scan
:.observe(element, { scan: false })
disables scanning the element's existing DOM, only newly added descendants will be handled..disconnect()
:.disconnect()
stops the observation.
Beware that scanning / observing the DOM may have noticeable performance drawbacks, so keep the scope as narrow as possible.
The scanning / observing does not pierce the Shadow DOM of elements, even if configured {mode: 'open')
.
If you want to enable lazy loading in Shadow DOM, you need do observe each ShadowRoot
.
This module uses Jest with jest-environment-jsdom.
If you run anything in NodeJS, be sure to use the --experimental-vm-modules
-Parameter since were dealing with native ES-Modules.
Eg.: node --experimental-vm-modules node_modules/jest/bin/jest.js
for testing.
npm test