Here's a simple MDL/CE/WC component that allows you to import HTML code.
Check the tests MDL.
Check the tests CE.
Check the tests WC.
Feel free to import HTML content by using
<div class="mdl-fragment" src="an_URI.html"></div>
or
<x-fragment src="an_URI.html"></x-fragment>
A fragment is an HTMLElement like a container. It's defined as follows:
<x-fragment id="fragment-id" src="an_URL.html"></x-fragment>
The expected behavior here's to fetch the HTML code at an_URL.html then put this code inside the fragment. A fragment exposes a simple interface:
var fragment = document.querySelector('#fragment-id');
fragment.addEventListener('load', e => {
// here you see that
// e.detail.fragment === fragment
// and this callback is reached once the HTML code
// from fragment.src has been fetched and appended to fragment
});
fragment.loaded.then((fragment_) => {
// here you see that
// fragment_ === fragment
// and this promise is ready once the HTML code
// from fragment.src has been fetched and appended to fragment
});
Scripts that are inside a <x-fragment>
see an special variable called document.currentFragment
. This variable behaves as document.currentScript
.
Also, due to lack of beforescriptexecute
event, here the scripts that are shipped inside a <x-fragment>
are treated in a special way. See below.
So, a fragment forces you to keep in mind an small set of features
- load event
- loaded promise
- currentFragment
- special threatment of scripts
Because of replacement all fragment's scripts with executable scripts, we encourage not to rely on script's events. Check this demo
The rough idea is that the fragment behaves like <script>
. If you import two or more times the same fragment, then it'll be fetched and executed as browser does with scripts
. There's no complex mechanism that allows to import a fragment, and reuse its code in somewhere else.
A.k.a the cousing of document.currentScript
. It works only once. So, while executing an script, the value of document.currentFragment
is the fragment caller. Once the promise of all scripts is completed, then document.currentFragment = null
.
Let's imagine an obvious case
<x-fragment src="URL.html">
<script src="something-that-could-be-important.js"></script>
<div class="loader-or-similar">
~
</div>
</x-fragment>
If the fragment would reset the content, then there's no guarrante that something-that-could-be-important won't brake the page after putting the fetched content from URL.html, right? By this simple reason, the eariler content cannot be touched.
Please, do not mix fragment-mdl.js with fragment-ce.js and fragment-wc.js. The last one is for old browsers. MDL is for Material Design Lite. CE is for Custom Elements and WC is for Web Components but instead of customElements.define
it uses document.registerElement
.
If this idea looks good to you and you want to implement it into your favorite framework, then feel free to send me a PR. I'll be glad to help you with testing and more.
This tool allows to import html code from a src = URI
where that resource is fetched via fetch. This means that fetch will handle CORS and some other cases.
While its expected that if a resource has been fetched and parsed successfully then a load event will be fire, this tool fetches and parses all the fragments and then will fire the load events in order of appearance. E.g in MDL:
<div class="mdl-fragment" src="fragmentA.html"> <!-- (1) -->
<!-- for the sake of the example, the fragmentA.html's content is: -->
<!-- <fragmentA.html> -->
<div class="mdl-fragment" src="fragmentB.html"> <!-- (2) -->
</div>
<!-- </fragmentA.html> -->
</div>
As intended here, (1) will dispatch load event after all its content and fragments has been fetched and parsed successfully. After (1) has finished, (2) will dispatch load event having all its content and fragments fetched and parsed successfully, and so on...
All scripts that belong to a fragment will be executed as async.
The load event is exposed as a Promise through loaded
property.
The resolution of relative URI is implemented by using a baseURI attribute that is assigned while fetching. Once the content has been fetched and parsed successfully, then this attribute is deleted.
The above doesn't apply for <scripts src=relativeURI></scripts>
inside a fragment. The reason is that the src's <script>
must be changed in order to let the browser's parser do its job.
You can provide global options for fetch request through meta-tag. In ex., you have options object:
let options = {
headers: {
"cache-control": "no-cache"
},
method: "GET"
}
You would be transform it to:
<!-- In custom-element (ce) -->
<meta x-fragment headers-cache-control="no-cache" method="GET">
<!-- In material design light (mdl) -->
<meta mdl-fragment headers-cache-control="no-cache" method="GET">
Also, you can divide options to multiple meta-tags:
<meta x-fragment headers-cache-control="no-cache">
<meta x-fragment method="GET">
$ git submodule init
$ git submodule update
$ npm start