I like the simplicity of hyperHtml and lit-html that use 'Tagged Template Literals' to only update the 'variable parts' of the template. Simple javascript and no need for virtual DOM code and the recommended immutable state.
I would like to try using custom elements with hyperHtml as simple as possible
with support of the <slot/>
principle in the templates, but without Shadow DOM. If I understand it right, slots are only possible with Shadow DOM?
Is there a way or workaround to have the <slot/>
principle in hyperHTML without using Shadow DOM?
<my-popup>
<h1>Title</h1>
<my-button>Close<my-button>
</my-popup>
Although there are benefits, some reasons I prefer not to use Shadow DOM:
With hyperHTML you can also use Custom Elements right away, optionally including a proper polyfill upfront when needed. Verify the result on Code Pen . There are no special rules to remember with Shadow DOM because behind the scenes hyperHTML uses a template element so if you bind a Shadow DOM element to hyperHTML, everything will just work.
There is nothing new to learn with hyperHTML other than standard JavaScript, HTML, or CSS. Its core features are built on top of template literals , where every interpolation is addressed once, as a unique DOM operation, and updated at lightning-speed when needed.
The goal of HyperHTMLElement is to be an ideal companion to hyperHTML itself. It removes all the repeated, boring and sometimes problematic setup steps needed to define Custom Elements. This makes their creation a no-brainer. Have a look at the comparisons VS Polymer . Below are some HyperHTMLElement class features:
The static Component.for (context [, uid]) utility helps solving tedious sub-components initialization. hyperHTML is easy to test in the browser and in NodeJS. If you're testing in NodeJS you will, however, need a JavaScript implementation of the DOM ( basicHTML or jsdom ).
Let me start describing what are slots and what problem these solve.
Just Parked Data
Having slots in your layout is the HTML attempt to let you park some data within the layout, and address it later on through JavaScript.
You don't even need Shadow DOM to use slots, you just need a template with named slots that will put values in place.
<user-data>
<img src="..." slot="avatar">
<span slot="nick-name">...</span>
<span slot="full-name">...</span>
</user-data>
Can you spot the difference between that component and the following JavaScript ?
const userData = {
avatar: '...',
nickName: '...',
fullName: '...'
};
In other words, with a function like the following one we can already convert slots into useful data addressed by properties.
function slotsAsData(parent) {
const data = {};
parent.querySelectorAll('[slot]').forEach(el => {
// convert 'nick-name' into 'nickName' for easy JS access
// set the *DOM node* as data property value
data[el.getAttribute('slot').replace(
/-(\w)/g,
($0, $1) => $1.toUpperCase())
] = el; // <- this is a DOM node, not a string ;-)
});
return data;
}
Slots as hyperHTML interpolations
Now that we have a way to address slots, all we need is a way to place these inside our layout.
Theoretically, we don't need Custom Elements to make it possible.
document.querySelectorAll('user-data').forEach(el => {
// retrieve slots as data
const data = slotsAsData(el);
// place data within a more complex template
hyperHTML.bind(el)`
<div class="user">
<div class="avatar">
${data.avatar}
</div>
${data.nickName}
${data.fullName}
</div>`;
});
However, if we'd like to use Shadow DOM to keep styles and node safe from undesired page / 3rd parts pollution, we can do it as shown in this Code Pen example based on Custom Elements.
As you can see, the only needed API is the attachShadow one and there is a super lightweight polyfill for just that that weights 1.6K min-zipped.
Last, but not least, you could use slots inside hyperHTML template literals and let the browser do the transformation, but that would need heavier polyfills and I would not recommend it in production, specially when there are better and lighter alternatives as shown in here.
I hope this answer helped you.
I have a similar approach, i created a base element (from HyperElement) that check the children elements inside a custom element in the constructor, if the element doesn't have a slot attribute im just sending them to default slot
import hyperHTML from 'hyperhtml/esm';
class HbsBase extends HyperElement {
constructor(self) {
self = super(self);
self._checkSlots();
}
_checkSlots() {
const slots = this.children;
this.slots = {
default: []
};
if (slots.length > 0) {
[...slots].map((slot) => {
const to = slot.getAttribute ? slot.getAttribute('slot') : null;
if (!to) {
this.slots.default.push(slot);
} else {
this.slots[to] = slot;
}
})
}
}
}
custom element, im using a custom rollup plugin to load the templates
import template from './customElement.hyper.html';
class CustomElement extends HbsBase {
render() {
template(this.html, this, hyperHTML);
}
}
Then on the template customElement.hyper.html
<div>
${model.slots.body}
</div>
Using the element
<custom-element>
<div slot="body">
<div class="row">
<div class="col-sm-6">
<label for="" class="">Name</label>
<p>
<a href="#">${model.firstName} ${model.middleInitial} ${model.lastName}</a>
</p>
</div>
</div>
...
</div>
</custom-element>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With