• Stars
    star
    702
  • Rank 64,450 (Top 2 %)
  • Language
    JavaScript
  • License
    ISC License
  • Created almost 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

The hyperHTML strength & experience without its complexity ๐ŸŽ‰

lighterhtml

Social Media Photo by Kristine Weilert on Unsplash

WebReflection status License: ISC Greenkeeper badge Blazing Fast

๐Ÿ“ฃ Community Announcement

Please ask questions in the dedicated discussions repository, to help the community around this project grow โ™ฅ


The hyperHTML strength & experience without its complexity ๐ŸŽ‰

Looking for something even smaller?

If you want 90% of functionalities offered by lightetrhtml for 2/3rd of its size, check ยตhtml, which is also used in "micro custom elements" ยตce library, hence ยตce-template too, plus "micro land" ยตland ๐Ÿฆ„


4.2 Highlights

  • the new ?boolean=${value} syntax from ยตhtml has landed in lighterhtml too. Feel free to read this long discussion to better understand why this syntax is better, or necessary.

faster than hyperHTML

Even if sharing 90% of hyperHTML's code, this utility removed all the not strictly necessary parts from it, including:

  • no Component, no define and/or intents, no connect or disconnect, and no promises (possibly in later on), everything these days can be easily handled by hooks, as example using the dom-augmentor utility
  • html content is never implicit, since all you have to do is to write html before any template when you need it. However, the {html: string} is still accepted for extreme cases.

Removing these parts made the output smaller in size (less than 6K) but it also simplified some underlying logic.

Accordingly, lighterhtml delivers raw domdiff and domtagger performance in an optimized way.

If you don't believe it, check the DBMonster benchmark ๐Ÿ˜‰

simpler than lit-html

In lit-html, the html function tag is worthless, if used without its render.

In lighterhtml though, the html.node or svg.node tag, can be used in the wild to create any, one-off, real DOM, as shown in this pen.

// lighterhtml: import the `html` tag and use it right away
import {html} from '//unpkg.com/lighterhtml?module';

// a one off, safe, runtime list ๐Ÿ‘
const list = ['some', '<b>nasty</b>', 'list'];
document.body.appendChild(html.node`
  <ul>${list.map(text => html.node`
    <li>${text}</li>
  `)}
  </ul>
`);

Strawberry on top, when the html or svg tag is used through lighterhtml render, it automatically creates all the keyed performance you'd expect from hyperHTML wires, without needing to manually address any reference: pain point? defeated! ๐Ÿพ

Available as lighterhtml-plus too!

If you are looking for Custom Elements like events out of the box, lighterhtml-plus is your next stop ๐Ÿ‘

How to import lighterhtml/-plus

Following, the usual multi import pattern behind every project of mine:

  • via global lighterhtml CDN utility: <script src="https://unpkg.com/lighterhtml"></script>, and const {render, html, svg} = lighterhtml
  • via ESM CDN module: import {render, html, svg} from 'https://unpkg.com/lighterhtml?module'
  • via ESM bundler: import {render, html, svg} from 'lighterhtml'
  • via CJS module: const {render, html, svg} = require('lighterhtml')

What's the API ? What's in the export ?

The module exports the following:

  • html tag function to create any sort of HTML content when used within a render call. It carries two extra utilities, html.for(ref[, id]), to hard-reference a specific node, and html.node to create one-off dom nodes in the wild without using the render.
  • svg tag function to create any sort of SVG content when used within a render call. It carries two extra utilities, svg.for(ref[, id]), to hard-reference a specific node, and svg.node to create one-off dom nodes in the wild without using the render.
  • render(node, fn|Hole) to pollute a node with whatever is returned from the fn parameters, including html or svg tagged layout
  • Hole class for 3rd parts (internal use)

You can test live a hook example in this Code Pen.

What's different from hyperHTML ?

  • the wired content is not strongly referenced as it is for hyperHTML.wire(ref[, type:id]) unless you explicitly ask for it via html.for(ref[, id]) or svg.for(ref[, id]), where in both cases, the id doesn't need any colon to be unique. This creates content hard wired whenever it's needed.
  • the ref=${object} attribute works same as React, you pass an object via const obj = useRef(null) and you'll have obj.current on any effect. If a callback is passed instead, the callback will receive the node right away, same way React ref does.
  • if the attribute name is aria, as in aria=${object}, aria attributes are applied to the node, including the role one.
  • deprecated: if the attribute name is data, as in data=${object}, the node.dataset gets populated with all values.
  • if the attribute name is .dataset, as in .dataset=${object}, the node.dataset gets populated with all values.
  • intents, hence define, are not implemented. Most tasks can be achieved via hooks.
  • promises are not in neither. You can update asynchronously anything via hooks or via custom element forced updates.
  • the onconnected and ondisconnected special events are available only in lighterhtml-plus. These might come back in the future but right now dom-augmentor replaces these via useEffect(callback, []). Please note the empty array as second argument.
  • an array of functions will be called automatically, like functions are already called when found in the wild
  • the Component can be easily replaced with hooks or automatic keyed renders
  • if a listener is an Array such as [listener, {once: true}], the second entry of the array will be used as option.
const {render, html} = lighterhtml;

// all it takes to have components with lighterhtml
const Comp = name => html`<p>Hello ${name}!</p>`;

// for demo purpose, check in console keyed updates
// meaning you won't see a single change per second
setInterval(
  greetings,
  1000,
  [
    'Arianna',
    'Luca',
    'Isa'
  ]
);

function greetings(users) {
  render(document.body, html`${users.map(Comp)}`);
}

Documentation

Excluding the already mentioned removed parts, everything else within the template literal works as described in hyperHTML documentation.

A basic example

Live on Code Pen.

import {render, html} from '//unpkg.com/lighterhtml?module';

document.body.appendChild(
  // as unkeyed one-off content, right away ๐ŸŽ‰
  html.node`<strong>any</strong> one-off content!<div/>`
);

// as automatically rendered wired content ๐Ÿคฏ
todo(document.body.lastChild);
function todo(node, items = []) {
  render(node, html`
  <ul>${
  items.map((what, i) => html`
    <li data-i=${i} onclick=${remove}> ${what} </li>`)
  }</ul>
  <button onclick=${add}> add </button>`);
  function add() {
    items.push(prompt('do'));
    todo(node, items);
  }
  function remove(e) {
    items.splice(e.currentTarget.dataset.i, 1);
    todo(node, items);
  }
}

What about Custom Elements ?

You got 'em, just bind render arguments once and update the element content whenever you feel like.

Compatible with the node itself, or its shadow root, either opened or closed.

const {render, html} = lighterhtml;

customElements.define('my-ce', class extends HTMLElement {
  constructor() {
    super();
    this.state = {yup: 0, nope: 0};
    this.render = render.bind(
      null,
      // used as target node
      // it could either be the node itself
      // or its shadow root, even a closed one
      this.attachShadow({mode: 'closed'}),
      // the update callback
      this.render.bind(this)
    );
    // first render
    this.render();
  }
  render() {
    const {yup, nope} = this.state;
    return html`
    Isn't this <strong>awesome</strong>?
    <hr>
    <button data-key=yup onclick=${this}>yup ${yup}</button>
    <button data-key=nope onclick=${this}>nope ${nope}</button>`;
  }
  handleEvent(event) {
    this[`on${event.type}`](event);
  }
  onclick(event) {
    event.preventDefault();
    const {key} = event.currentTarget.dataset;
    this.state[key]++;
    this.render();
  }
});

Should I ditch hyperHTML ?

Born at the beginning of 2017, hyperHTML matured so much that no crucial bugs have appeared for a very long time.

It has also been used in production to deliver HyperHTMLElement components to ~100M users, or to show W3C specifications, so that in case of bugs, hyperHTML will most likely be on the fast lane for bug fixes, and lighterhtml will eventually follow, whenever it's needed.

On top of this, most modules used in lighterhtml are also part of hyperHTML core, and the ./tagger.js file is mostly a copy and paste of the hyperHTML ./objects/Update.js one.

However, as tech and software evolve, I wanted to see if squashing together everything I know about template literals, thanks to hyperHTML development, and everything I've recently learned about hooks, could've been merged together to deliver the easiest way ever to declare any non-virtual DOM view on the Web.

And this is what lighterhtml is about, an attempt to simplify to the extreme the .bind(...) and .wire(...) concept of hyperHTML, through a package that requires pretty much zero knowledge about those internals.

lighterhtml is also relatively new, so that some disabled functionality might come back, or slightly change, but if you like the idea, and you have tested it works for your project, feel free to ditch hyperHTML in favor of lighterhtml, so that you can help maturing this project too.

Should I use micro html instead ?

ยตhtml is a great way to start playing around with most lighterhtml features. As it's simply a subset, you can eventually switch to lighterhtml later on, whenever you miss, or need, some extra feature.

For a complete comparison of features and libraries around my repositories, please have a look at this gist.


History and changes

This session covers all major breaking changes and added features.

V4 Breaking Changes

I am afraid this major was necessary due recent bugs/discoveries that made me rethink some practice and patch.

  • the recently introduced data helper could conflict with some node such as <object>, hence it has been replaced by the .dataset utility. Since element.dataset = object is an invalid operation, the sugar to simplify data- attributes is now never ambiguous and future-proof: <element .dataset=${...} /> it is.
  • all cross browsers normalizations and features detection to make the template literal unique has been removed, as these were causing more problems than these were supposed to solve. If you are targeting IE 11 or older browsers, be sure you use Babel 7 to transpile your production code. If you are using TypeScript, be sure you use Babel 7 to transpile your code, as TS has always been broken with transpiled template literals (and classes, and ...).
  • the good old domdiff that served me well, and it still does, has been replaced by its little udomdiff brother, allowing lighterhtml to weight 1K less than before, still keeping lightning fast performance.

Because of these breaking changes, all libraries around lighterhtml will gradually bump major version too, pointing at this paragraph of the README.

V3 Declarative data and aria attributes.

Since the introduction of .setter=${value} made special cases such as data=${...} and props=${...} redundant, as it's always possible to simply attach any kind of data via .data=${...} or .props=${...}, version 3 enhances the declarative power of the template to HTML translation.

// the aria special case
html`<div aria=${{labelledBy: 'id', role: 'button'}} />`;
//=> <div aria-labelledby="id" role="button"></div>

// the *deprecated* dataset special case
html`<div data=${{key: 'value', otherKey: 'otherValue'}} />`;
//=> <div data-key="value" data-other-key="otherValue"></div>

// the *new* dataset special case
html`<div .dataset=${{key: 'value', otherKey: 'otherValue'}} />`;
//=> <div data-key="value" data-other-key="otherValue"></div>

This means the previous data=${...} behavior should be substituted with .dataset=${...} and it's now possible to better reflect declarative intents in nodes, simplifying both data-* attributes and aria-* ones.

Please note using data-name=${value}, as well as aria-name=${value} is still handled like any other regular attribute, hence it will work as expected, actually faster when the values don't change frequently, as both aria and data special cases simply loop through the object keys and assign their values to node's attributes.

V2.1 Introducing A New Listener Feature

Until version 2.1, there was no way to define different options for any listener. The el.addEventListener(type, listener, false) was the only kind of operation possible behind the scene.

In v2.1 though, whenever a second option is needed, it is now possible to pass an Array instead, in the form [listener, {once: true}], as example, or [listener, true] to use capture instead of bubbling, and so on and so forth.

Bear in mind, specially for the once case, if the listener is different per each update, like onclick=${[() => stuff(), {once: true}]}, it will be set each time that update happens, so that in this case is better to use always the same listener, either via outer scope callback, or via reference, using useRef and the handleEvent pattern, as example.

If you never needed to add a different second option, there is nothing you should do, everything will work exactly as it did before.

V2 Breaking Changes & Improvements

Breaking

  • dropped the ambiguous ability to produce nodes when no render(...) is invoked. When needed, which is the minority of the cases, you need to explicitly use html.node or svg.node, instead of just html or svg. For every other cases, use render(where, what).
  • the render(where, what) does not need a callback anymore. You can now render(node, html`<p>content</p>`) right away. If a callback is provided, that will still be invoked.
  • removed useHook as it's unnecessary since you can use useRef through html.for(...) or svg.for(...) within any useRef provided by your library of choice (i.e. dom-augmentor)
  • the recently introduced inner.html/svg has been removed, as completely unnatural and error prone (just use html anywhere, it'll work).

Improvements

  • a fundamental core-logic implementation that was trashing any node after one or more collections has been refactored and fixed. The current logic create a stack per each array found down the rendering road, isolating those DOM updates per stack. This means that performance have been improved, and GC operations reduced.
  • html and svg template literals tags, now offer both .for(ref[, id]) and .node, to either retain the same content (keyed render) or create fresh new nodes out of the box as one-off operation (via .node).
  • slightly reduced code size, which is always nice to have, after a refactoring

V1 Changes + New Feature

Removed transform export and made default domtagger customizable via custom export.

import { custom } from 'lighterhtml';

const { html, render } = custom({

  // the domtagger attributes handler
  attribute: callback => (node, name, original) => {
    // return a function that will handle the attribute value
    // the function will receive just the new value
    if (name === 'double')
      return value => {
        node[name] = value + value;
      };
    // the received callback is usable as return fallback
    return callback(node, name, original);
  },

  // the domtagger any-content handler
  any: callback => (node, childNodes) => {
    // return a function that will handle handle all special cases
    // the function will receive just the new *hole* value
    if (node.nodeName === 'CUSTOM') {
      return value => {
        node.appendChild(value);
      };
    }
    // the received callback is usable as return fallback
    return callback(node, childNodes);
  },

  // the domtagger text for text only cases
  text: callback => (node) => {
    // return a function that will handle handle text content cases
    // the function will receive just the new text value
    if (node.nodeName === 'WRAP') {
      return value => {
        node.textContent = `(${value})`;
      };
    }
    // the received callback is usable as return fallback
    return callback(node);
  },

  // optionally you can use the special transform handler too
  // in this case, and in V1, the callback is just the String one
  transform: callback => markup => callback(markup),

  // same goes for convert, with the callback being the one
  // originally used to "convert" the template from Array to HTML
  // see: https://github.com/WebReflection/domtagger/issues/17#issuecomment-526151473
  convert: callback => template => callback(template)
});

More Repositories

1

hyperHTML

A Fast & Light Virtual DOM Alternative
HTML
3,028
star
2

linkedom

A triple-linked lists based DOM implementation.
HTML
1,156
star
3

document-register-element

A stand-alone working lightweight version of the W3C Custom Elements specification
JavaScript
1,131
star
4

dom4

Modern DOM functionalities for every browser
JavaScript
929
star
5

flatted

A fast and minimal circular JSON parser.
JavaScript
893
star
6

url-search-params

Simple polyfill for URLSearchParams standard
JavaScript
765
star
7

uhtml

A micro HTML/SVG render
JavaScript
707
star
8

JSONH

Homogeneous Collection Compressor
JavaScript
618
star
9

circular-json

JSON does not handle circular references. Now it does
JavaScript
599
star
10

viperHTML

Isomorphic hyperHTML
JavaScript
318
star
11

heresy

React-like Custom Elements via V1 API builtin extends.
JavaScript
271
star
12

es6-collections

Map, WeakMap, and Set fast/simple shim for Harmony collections
JavaScript
253
star
13

neverland

React like Hooks for lighterhtml
JavaScript
241
star
14

wicked-elements

Components for the DOM as you've never seen before
JavaScript
235
star
15

eddy

Event Driven JS
JavaScript
211
star
16

dblite

sqlite for node.js without gyp problems
JavaScript
209
star
17

domdiff

Diffing the DOM without virtual DOM
JavaScript
197
star
18

hyperHTML-Element

An extensible class to define hyperHTML based Custom Elements.
JavaScript
195
star
19

benja

Bootable Electron Node JS Application
194
star
20

uce

ยตhtml based Custom Elements
JavaScript
183
star
21

usignal

A blend of @preact/signals-core and solid-js basic reactivity API
JavaScript
176
star
22

highlighted-code

A textarea builtin extend to automatically provide code highlights based on one of the languages available via highlight.js
HTML
172
star
23

restyle

JavaScript
167
star
24

testardo

a browser and OS agnostic web driver for mobile and desktop
JavaScript
166
star
25

sqlite-worker

A simple, and persistent, SQLite database for Web and Workers.
JavaScript
159
star
26

augmentor

Extensible, general purpose, React like hooks for the masses.
JavaScript
135
star
27

uhooks

micro hooks: a minimalistic client/server hooks' implementation
JavaScript
127
star
28

polpetta

Polpetta, any folder is served spiced
JavaScript
125
star
29

basicHTML

A NodeJS based, standard oriented, HTML implementation.
JavaScript
123
star
30

udomdiff

An essential diffing algorithm for ยตhtml.
JavaScript
117
star
31

pocket.io

A minimalistic version of socket.io that weights about 1K instead of 60K.
JavaScript
112
star
32

caller-of

The tiniest yet most powerful JS utility ever :D
HTML
102
star
33

uland

A ยตhtml take at neverland
JavaScript
101
star
34

uce-template

A Vue 3 inspired Custom Elements toolless alternative.
JavaScript
100
star
35

json.hpack

JSON Homogeneous Collections Packer
C#
95
star
36

wru

essential unit test framework
JavaScript
95
star
37

html-escaper

A module to escape/unescape common problematic entities done the right way.
JavaScript
95
star
38

udomsay

A stricter, signals driven, ESX based library
JavaScript
95
star
39

db

JavaScript
94
star
40

import.js

A dynamic import() polyfill
JavaScript
93
star
41

regular-elements

Custom Elements made available for any node, and through CSS selectors
JavaScript
91
star
42

proxy-pants

Secured and reliable Proxy based utilities for more or less common tasks.
JavaScript
90
star
43

ucompress

A micro, all-in-one, compressor for common Web files.
JavaScript
90
star
44

archibold.io

archibold.io
Shell
85
star
45

jsgtk

A simplified approach to GJS for Node.JS and JavaScript developers.
JavaScript
85
star
46

heresy-ssr

๐Ÿ”ฅ heresy ๐Ÿ”ฅ Server Side Rendering
JavaScript
84
star
47

hypersimple

The easiest way to use hyperHTML
JavaScript
83
star
48

asbundle

A minimalistic JS bundler
JavaScript
77
star
49

introspected

Introspection for serializable arrays and JSON friendly objects.
HTML
77
star
50

node-gtk

GNOME Gtk+ bindings for NodeJS
C++
74
star
51

i18n-utils

The i18n tag function utilitities
JavaScript
73
star
52

viper-news

viperHTML version of the Hacker News app.
JavaScript
72
star
53

ucdn

A ยตcompress based CDN utility, compatible with both Express and native http module
JavaScript
67
star
54

electroff

A cross browser, electron-less helper, for IoT projects and standalone applications.
JavaScript
64
star
55

builtin-elements

A zero friction custom elements like primitive.
JavaScript
62
star
56

nonchalance

The easiest way to augment DOM builtin elements.
JavaScript
61
star
57

universal-mixin

A mixin usable for both generic objects and decorators.
JavaScript
59
star
58

attachshadow

An iframe based Shadow DOM poorlyfill
JavaScript
59
star
59

ucontent

An SSR HTML content generator.
JavaScript
58
star
60

dom-augmentor

Same as augmentor but with DOM oriented useEffect handling via dropEffect.
JavaScript
56
star
61

ascjs

ES2015 to CommonJS import/export transformer
JavaScript
55
star
62

geo2city

Basic offline reverse geocode
JavaScript
53
star
63

vanilla-elements

A Minimalistic Custom Elements Helper.
JavaScript
51
star
64

wrist

Minimalistic utility for generic one/two ways data bindings.
HTML
51
star
65

screenfit

A cross platform, cross WebView, solution to fit 100% any Web page.
HTML
50
star
66

html-parsed-element

A base custom element class with a reliable `parsedCallback` method.
HTML
50
star
67

sqlite-tag

Template literal tag based sqlite3 queries.
JavaScript
49
star
68

jsdon

A DOM serializer based on LinkeDOM idea
HTML
48
star
69

echomd

A terminal oriented MD like syntax
JavaScript
48
star
70

dom-class

A lightweight, cross browser, simplification of WebComponents.
JavaScript
46
star
71

cloner

Cloning ES5+ objects in a shallow or deep way
JavaScript
46
star
72

css-proxied-vars

The easiest way to set, read, or update, CSS variables per each element.
JavaScript
46
star
73

event-target

The EventTarget Class Polyfill.
HTML
46
star
74

hn

Isomorphic Hacker News
JavaScript
45
star
75

proxied-worker

A tiny utility to asynchronously drive a namespace exposed through a Worker.
JavaScript
45
star
76

Database

Web SQL Storage Made Easy
JavaScript
44
star
77

promise

Abortable and Resolvable Promises.
JavaScript
43
star
78

poorlyfills

Simplified, partial, and poor ES6 collections polyfills, targeting IE9+ and older mobile browsers.
HTML
43
star
79

babel-plugin-transform-builtin-classes

A fix for the infamous Babel #4480 bug.
JavaScript
43
star
80

redefine

lightweight utility for smart object properties definition
JavaScript
42
star
81

nativeHTML

coming soon
JavaScript
42
star
82

bidi-sse

Bidirectional Server-sent Events
JavaScript
42
star
83

domtagger

The hyperHTML's template literal parser
JavaScript
41
star
84

hooked-elements

wickedElements ๐Ÿง™ with render hooks
JavaScript
41
star
85

classtrophobic

Breaking JS Class Constrains
HTML
41
star
86

a-route

Express like routing as Custom Element or standalone
JavaScript
40
star
87

header-snippets

A collection of snippets to put in your header.
HTML
40
star
88

life-diary

your albums, your journey, your data
JavaScript
40
star
89

static.email

The easiest way to send emails on the Web
JavaScript
39
star
90

lazytag

Lazy loading Custom Elements and their styles without even thinking about it.
JavaScript
38
star
91

es-class

ECMAScript 3 to 6 compatible Class definition
JavaScript
38
star
92

tiny-cdn

A tiny static files serving handler
JavaScript
37
star
93

broadcast

Notification channel for the past, the present, and the future.
JavaScript
37
star
94

p-cool

Pretty Cool Elements
JavaScript
36
star
95

monthly

A simplified way to show a calendar month in any console.
HTML
36
star
96

jsx2tag

Enable JSX for Template Literal Tags based projects.
JavaScript
36
star
97

consolemd

Bringing echomd to console.
JavaScript
36
star
98

common-js

CommonJS + module.import() for any Browser
JavaScript
35
star
99

qsa-observer

handle elements lifecycle through CSS selectors
JavaScript
35
star
100

create-viperhtml-app

A basic viperHTML + hyperHTML setup
JavaScript
35
star