Remote Single file component without any framework
Published: 2023-03-13 08:07:13

I read an awesome article I and wanted to share it with the community.

[Implementing single-file Web Components](https://ckeditor.com/blog/implementing-single-file-web-components/)

The idea is to be able to load and run  a remote single file component like the one below.

This one is accessible from the following url :
[https://blog.comandeer.pl/assets/jednoplikowe-komponenty/HelloWorld.wc
](https://blog.comandeer.pl/assets/jednoplikowe-komponenty/HelloWorld.wc)

Notice the extension.


```html
<template>
	<div class="hello">
		<p>Hello, world! My name is <slot></slot>.</p>
	</div>
</template>

<style>
	div {
		background: red;
		border-radius: 30px;
		padding: 20px;
		font-size: 20px;
		text-align: center;
		width: 300px;
		margin: 0 auto;
	}
</style>

<script>
	export default {
		name: 'hello-world',
		onClick() {
			alert( `Don't touch me!` );
		}
	};
</script>
```



Here is the loader :

```js
let loadComponent = (function() {
	function fetchAndParse( URL ) {
		return fetch( URL ).then( ( response ) => {
			return response.text();
		} ).then( ( html ) => {
			const parser = new DOMParser();
			const document = parser.parseFromString( html, 'text/html' );
			const head = document.head;
			const template = head.querySelector( 'template' );
			const style = head.querySelector( 'style' );
			const script = head.querySelector( 'script' );

			return {
				template,
				style,
				script
			};
		} );
	}

	function getSettings( { template, style, script } ) {
		const jsFile = new Blob( [ script.textContent ], { type: 'application/javascript' } );
		const jsURL = URL.createObjectURL( jsFile );
    
		function getListeners( settings ) {
			return Object.entries( settings ).reduce( ( listeners, [ setting, value ] ) => {
				if ( setting.startsWith( 'on' ) ) {
					listeners[ setting[ 2 ].toLowerCase() + setting.substr( 3 ) ] = value;
				}

				return listeners;
			}, {} );
		}

		return import( jsURL ).then( ( module ) => {
			const listeners = getListeners( module.default );

			return {
				name: module.default.name,
				listeners,
				template,
				style
			}
		} );
	}

	function registerComponent( { template, style, name, listeners } ) {
		class UnityComponent extends HTMLElement {
			connectedCallback() {
				this._upcast();
				this._attachListeners();
			}

			_upcast() {
				const shadow = this.attachShadow( { mode: 'open' } );

				shadow.appendChild( style.cloneNode( true ) );
				shadow.appendChild( document.importNode( template.content, true ) );
			}

			_attachListeners() {
				Object.entries( listeners ).forEach( ( [ event, listener ] ) => {
					this.addEventListener( event, listener, false );
				} );
			}
		}

		return customElements.define( name, UnityComponent );
	}      

	function loadComponent( URL ) {
		return fetchAndParse( URL ).then( getSettings ).then( registerComponent );    
	}

	return loadComponent; 
})()
let url = "https://blog.comandeer.pl/assets/jednoplikowe-komponenty/HelloWorld.wc" + `?x=${Math.random()}`     


loadComponent(url) .then( ( component ) => {
					console.log( 'Component loaded' ); 
          document.body.insertAdjacentHTML( 'beforeend', '<hello-world>Comandeer</hello-world>' );
				} );

document.querySelector( 'button' ).addEventListener( 'click', () => {
  document.body.insertAdjacentHTML( 'beforeend', '<hello-world>Comandeer</hello-world>' );
} );
```



You can see a demo here [SFC](https://flems.io/#0=N4IgtglgJlA2CmIBcA2AnAOgAwEYA0IAZhAgM7IDaoAdgIZiJIgYAWALmLCAQMYD21NvEHIQAHgBGAVzZsBAPgA61AAQqAgjBXV4AdxX8wABwHC2ysQHppshdxCl4CHmwgDyTHEhxoQAXzwaekZmACtyXgEhESYENhVYPlooAGE+Y1NBFQBeFQAKQilqFzdqPIBKFWBlRTZC4tcBFUJ4Nh4WdWooAAVaACdHPJUAVQAlABkVSurqWtq+1qk+1Ra2liGxyfKMNhZhIaGF0hNqRymc+Sqa8xuFtiXVI5PHHfgADzYKgG5r2r8pnZ7Mr5FTsTjnbKXGZzG78U7xIz9Rx9HLaPQqAAiAHkALK9AbwPrfX6w9zxKB8HhSBhZXKIgl9DD0xwAMT66QAymw+hBqABzIZg2B4FQAciEH0sQtFUx+sxutThpHie2SqIpVJpbFY8GScphirJKiExlgtCEqNVUAwAEcpISAJ4cpzwFx8Ilik1GM1CGXlfUKtpG5UOhCW3XWu2O53OOQe0UhhB+gMwpXxUg8HlGeK5K22+19J0ut3xjNZtjJmrymF3B5XauBr0++B4EkwxMtts3MsQbNdvwptj-f3XPxV2r1EpNPmtZ2yXl80hDYDG+Cm80tlQdkU97MqYf11NG8IskjwVE6fQAIUSEiGFC3md72olbDSgjMKgAuiKV2wHUY8BIGKtBGN6EA8OapSWKEtAAG60LuFb7rKJJpio4SbKimwYJmupCFiEihK6bCbEMJ5nqhqhqCoJKTo0qgzmw4wQMqwiEkuW6tK4-KkOc0KBrWywqIRxEuBgZg8vAnGOPOvEAgsUBSDw8AHAkrHRBxIoPrJPF8iKCGwPa34QlCXa1BAhAgrpC4YMq-RsKQADqEC7EMooCH6-HmTcsAaexAw6dxC4PgATN+Ox8OMfC6ISKSIaplQANRcXJfJ2VIEjKh6ADM5xfqihn2oOMJjrMDY1oswl+WxOgDCVQ6-sOg5lSSQmqBAGR9J8GGkFh2y7PsIJgHwSlhpUkKHoG6E1ZpAyokxLG1RxQwjWN8AYFA8CELQUiwPEI7lQa3JVaoAnHXQDDAWte0bVtO17dql2dhVvn+XVpCtq9tRNhuX3HbUHb9iSzWjuOdRFFOjzwHy-l9GkGQ6IIy6ruuQg7v+CAis9IqzQFfEHudbRmqQfHDNQrkOgjJyfu80RQHxAASAAqOLjAAoggWpTUe1A6C48CpLQsCwBItA8AA1hUPONiwrEYAA+lIRiQcqxLfbIcukIr5psOLLBLXNS6HcdrWvUrKuIZ80w+ehpAsMkMWors8u6-rHIOxSugo2tQFinwgHUDKoNHYGgOezFGCgYHqRy7AUBDB2uGJDoAByo2qcafTGZUJth45Ee6FHYHCLHJAJyoGrUmYGCdSY3Xp1tQy-UIuFRGYIrcjnVGm+DMIK277SG-j0tEzCokkRJghSZxeMfQChDuuz+tqQ+8BwR36nLSiBUTWZGs-VrUcwOzG+CMPdVDOvm9z4SIo7bAZx56bPeBq1Nxm5V9zCVSyrpJza4zDa3uryTOOMRgU3-NTTIB0Az-BomocG9FSgJCSKkdINNkYjAmN5Bs7VmitHaJ0HoSJM79UBENJic49J8QGkCQ4MM4bQKRrAhBYNQ74MSMkZhZgvi0WoH4coFRlBxBUEsWAqJFAgHYGwIwpAkCWGsIkdKhhaBdHgISJksBLCIVkqQGCgtqABz8hLGK8AAC0pjEZmAdJYRmThEhOXdPHDAugeBSJUClAABgAfjeNkAAJMAHE5oWAYD6GoikYAKh+C8Qg-hVYuHoOscjcRlQKHAiGIYTBB0Lgy2OkqPgCAMDKPcjwrISTBbJn4fE+JVctQYAkKNB0tdTiEjYJoWCKlBAszZu5CQ213QaK6KKEUooxB7BFnwcxuhnFQHkAjSJGi+hWEmYkGZcz5CVg1iHZQ9Sa5RkLDGEi7p+kyDkEHAEyQoCnzMBfQk7keB+UlqM-Ie96xqH2YIRpzTWnIg6VALpZhenjH6YMhYpdXnjLWdM2ZfR44LPSEswkqyHGws2dskO9hHCxlKB4EAAB2FA3gcDmKJUgAAHDgfwgQQDPVELhUm9g4TRDYKIfwP46XBFEByFkKR-BAA)