be-active
Activate template content.
<template be-active>
<script id=blah/blah.js></script>
<link href="https://fonts.googleapis.com/css?family=Indie+Flower">
</template>
The content inside becomes imported into the current page.
Why do we need this functionality?
- Outside shadow DOM, when a script tag is inserted, the browser "picks it up" and loads the dependency. Not so inside Shadow DOM (at least if the tag was cloned from a template). This strangely inconsistent behavior can be inconvenient, especially if we want to lazy load / prioritize how scripts are loaded.
- Font dependencies of a web component have to be added outside of any shadow DOM.
- If a web component separates the JS payload from the file containing HTML (like a JSON file or an actual HTML file), it is convenient to list the dependencies in the file that actually uses them.
- Lazy loading dependencies becomes much more natural if the dependencies are closely positioned to their actual use. So even if HTML Modules become a thing, this could still be useful in that context.
- Added plus of placing dependencies close to their use: Developer can avoid vertiginousness, and not have to scroll up and down so much while adding imports.
- This allows HTML streams to be used both as standalone web pages and also work as part of an embedded stream within the app / page via web components.
Priors
Jquery's load function provides support for loading script as well. As does the millions hits / month shoelace include component.
lazy-imports also shares similar goals.
be-active is a declarative alternative to xtal-sip.
It is also a non-blasphemous alternative to part of what templ-mount does.
Details
Adopting this approach means, for now, your JavaScript references cannot benefit from local bundling tools. Plugins for bundling tools are not yet available.
Regardless, the solution can already work with both import maps and CDN's.
Each script reference must have an id. Inner inline script will be ignored. You can add type=module if you wish, but it doesn't matter -- this only works for ES Modules, so that is assumed.
By default, CDN provider jsdelivr.com is used in the case that import maps fail. However, alternative CDN's, such as cdn.skypack.dev, or unpkg.com or maybe an internal CDN, can be used.
The required id is used in two ways: If the id matches to a link rel=preload (or link rel=anything, really) be-active will get the href from that link. Optional hash integrities will be copied from the link tag. Same with crossorigin settings. This only applies to fonts [TODO -- confirm this]
Also, use of an id will block other instances from trying to resolve to something else. The id should be the bare import specifier that is recommended when referencing the resource in code. This helps to avoid cluttering the head tag, which is where the script tags are placed.
What be-active does:
For each script tag found inside the be-active adorned template:
- If the id of the script tag matches a link tag, a dynamic import of the href is added to the head tag. The link tag is removed. End of story.
- One script tag will be created in the head tag, with the same id (assuming such an id doesn't already exist).
- The id will be turned into a dynamic import inside the head script tag. However, the import attempt will be intercepted if it fails.
- Should the import fail, the src reference will be prepended with the CDN base url, and that will be tried. An optional postfix parameter will be specifiable.
- If the second attempted import fails, it will be logged to the console natively.
- If, in the future, import maps are enhanced to provide an api for resolving (or failing to resolve) a path, then the try catch won't be necessary. [TODO]
For each link tag:
- If the href of the link already exists as an id of a link rel=stylesheet outside any shadow DOM, do nothing.
- If the href of the link inside the template matches the id of a link rel=preload outside any shadow DOM, change the value of the rel from preload to stylesheet.
- Else clone the link tag inside the template, copy the href attribute to the id, insert in the head tag
Options
Specifying an alternative CDN base url (only applies to script references):
Approach 1:
<html>
<head>
<link rel=preconnect id=be-active/baseCDN href=https://cdn.skypack.dev>
</head>
<body>
...
<template be-active>
<script id=blah/blah.js></script>
<link rel=stylesheet href="https://fonts.googleapis.com/css?family=Indie+Flower">
</template>
</body>
</html>
Approach 2:
<template be-active=https://cdn.skypack.dev>
<script id=blah/blah.js></script>
<link rel=stylesheet href="https://fonts.googleapis.com/css?family=Indie+Flower">
</template>
Approach 2 might be better if only one CDN supports some JS language feature you are using. Adopt approach 1 when the others catch up.
Note that with approach 1, it affects all components that rely on be-active, where the CDN base url isn't explicitly specified.
CDNpostFix - specify a string to append to end of CDN url.
Example:
<template be-active='{
"baseCDN": "https://unpkg.com/",
"CDNPostfix": "?module"
}'>
<script id=blah/blah.js></script>
<link rel=stylesheet href="https://fonts.googleapis.com/css?family=Indie+Flower">
</template>
Specify version for CDN backup
<template>
<script data-version=1.2.34 id=blah/blah.js></script>
</template>
Prioritize via waiting
Each script tag can have a comma delimited list of web component definitions it should wait for before loading.
<script data-when=my-custom-element-1,my-custom-element-2>
Block duplicate web component references
Normally, if web components are using ES modules, and all users of the dependency use ES modules syntax, and all resolve to the same version, then there is no extra network load imposed on the browser. So developers don't need to worry about including import statements to libraries when in fact in some deployment scenarios, those references will already be imported from third party components. No extra downloads occur.
But there are scenarios where a web component dependency may be defined in more than one way -- for example a web component provides both an SSR/HTML reference, and an alternative JS reference.
In that case, we could end up doubling the network load, potentially seeing errors or warnings about multiple web component registrations with the same name, etc.
To minimize the chances of this happening, add an additional optional attribute to the script tag:
be-active will check before requesting the resource that there is no web component already registered with name "blah-blah", and if so, avoid doing anything.
Lazy Loading
While be-active supports some amount of lazy loading, based on the placement of the tag / visibility of the tag, it does load the dependencies as soon as it is visible. To only load a dependency when a custom element itself that depends on the dependency becomes visible, that use case is to be covered by be-tagmemic.
Media Queries
To be covered by be-tagmemic.
Bundled References
To be covered by be-tagmemic.