

GitHub - loreanvictor/rehtm: Create HTML with HTM
source link: https://github.com/loreanvictor/rehtm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

loreanvictor/rehtm
import { html, ref } from 'rehtm'
let count = 0
const span = ref()
document.body.append(html`
<div onclick=${() => span.current.innerHTML = ++count}>
Clicked <span ref=${span}>${count}</span> times!
</div>
`)
Hydration for pre-rendered content (e.g. SSR)
Functions as Event Listeners
Element references
Object properties for custom elements
Cached HTML templates for performance
Extensions for custom attribute and node types
Contents
Installation
Node:
npm i rehtm
Browser / Deno:
import { html } from 'https://esm.sh/rehtm'
Usage
Render DOM:
import { html } from 'rehtm'
const name = 'World'
document.body.append(html`<div>Hellow ${name}!</div>`)
Add event listeners:
document.body.append(html`
<button onclick=${() => alert('CLICKED!')}>
Click ME!
</button>
`)
Use
ref()
to get references to created elements:
import { ref, html } from 'rehtm'
const el = ref()
document.body.append(html`<div ref=${el}>Hellow World!</div>`)
console.log(el.current)
// <div>Hellow World!</div>
Set object properties:
const div = ref()
html`<div ref=${div} prop=${{x: 2}}>Some content</div>`
console.log(div.current.prop)
// > { x: 2 }
If the object properties are set on a custom element with a .setProperty()
method, then that method will be called instead:
import { define, onProperty } from 'minicomp'
import { html, ref } from 'rehtm'
define('say-hi', () => {
const target = ref()
onProperty('to', person => target.current.textContent = person.name)
return html`<div>Hellow <span ref=${target} /></div>`
})
const jack = { name: 'Jack', ... }
const jill = { name: 'Jill', ... }
document.body.append(html`
<say-hi to=${jack} />
<say-hi to=${jill} />
`)
rehtm creates HTML templates for any string literal and reuses them when possible. The following elements are all constructed from the same template:
html`<div class=${'foo'}>Hellow ${'World'}</div>` html`<div class=${'bar'}>Hellow ${'Welt'}</div>` html`<div class=${'baz'}>Hellow ${'Universe'}</div>`
Hydration
Use
template
s to breath life into content already rendered (for example, when it is rendered on the server):
import { template, ref } from 'rehtm'
const span = ref()
let count = 0
// 👇 create a template to hydrate existing DOM:
const tmpl = template`
<div onclick=${() => span.current.textContent = ++count}>
Clicked <span ref=${span}>${count}</span> times!
</div>
`
// 👇 hydrate existing DOM:
tmpl.hydrate(document.querySelector('div'))
<div>
Clicked <span>0</span> times.
</div>
Use
.hydateRoot()
to hydrate children of an element instead of the element itself:
const tmpl = template`
<i>${'some stuff'}</i>
<b>${'other stuff'}</b>
`
tmpl.hydrateRoot(document.querySelector('#root'))
<div id="root">
<i>This will be reset.</i>
<b>This also.</b>
</div>
rehtm can hydrate DOM that is minorly different (for example, elements have different attributes). However it requires the same tree-structure to be able to hydrate pre-rendered DOM.
Use
.create()
for using a template to create elements with different values:
const tmpl = template`<div>Hellow ${'World'}</div>`
tmpl.create('Jack')
// > <div>Hellow Jack</div>
html
template tag also creates templates and uses.create()
method to generate elements. It caches each template based on its string parts, and reloads the same template the next time it comes upon the same string bits.
Extension
You can create your own html
and template
tags with extended behavior. For that, you need a baseline DOM factory, extended with some extensions, passed to the build()
function. For example, this is (roughly) how the default html
and template
tags are generated:
import {
build, // 👉 builds template tags from given DOM factory, with caching enabled.
extend, // 👉 extends given DOM factory with given extensions.
domFactory, // 👉 this is the baseline DOM factory that just creates elements.
// 👉 this extension allows using functions as event listeners
functionalEventListenersExt,
// 👉 this extension allows setting object properties
objectPropsExt,
// 👉 this extension enables references
refExt,
} from 'rehtm'
const { html, template } = build(
extend(domFactory,
functionalEventListenersExt,
objectPropsExt,
refExt,
)
)
An extension can extend any of these four core functionalities:
-
create(type, props, children, fallback, self)
Is used when creating a new element.children
is an already processed array of values. Should return the created node. -
attribute(node, name, value, fallback, self)
Is used when setting an attribute on an element (acreate()
extension might bypass this). -
append(node, value, fallback, self)
Is used when appending a child to a node (acreate()
extension might bypass this). Should return the appended node (or its analouge). -
fill(node, value, fallback, self)
Is used when hydrating a node with some content.
Each method is given a
fallback()
, which it can invoke to invoke prior extensions (or the original factory):
const myExt = {
attribute(node, name, value, fallback) {
if (name === 'some-attr') {
// do the magic
} else {
// not our thing, let others set this
// particular attribute
fallback()
}
}
}
You can also call fallback()
with modified arguments:
fallback(node, modify(name), value.prop)
Each method is also given a
self
object, which represents the final DOM factory. It can be used when you need to
invoke other methods of the host factory:
const myExt = {
create(tag, props, children, fallback, self) {
if (tag === 'my-custom-thing') {
const node = fallback('div')
self.attribute(node, 'class', 'my-custom-thingy', self)
if (props) {
for (const name in props) {
self.attribute(node, name, props[name], self)
}
}
// ...
} else {
return fallback()
}
}
}
You can see some extension examples here. For examle, this is how the functional event listeners extension works:
export const functionalEventListenerExt = {
attribute(node, name, value, fallback) {
if (name.startsWith('on') && typeof value === 'function') {
const eventName = name.slice(2).toLowerCase()
node.addEventListener(eventName, value)
} else {
fallback()
}
}
}
Contribution
You need node, NPM to start and git to start.
# clone the code
git clone [email protected]:loreanvictor/minicomp.git
# install stuff
npm i
Make sure all checks are successful on your PRs. This includes all tests passing, high code coverage, correct typings and abiding all the linting rules. The code is typed with TypeScript, Jest is used for testing and coverage reports, ESLint and TypeScript ESLint are used for linting. Subsequently, IDE integrations for TypeScript and ESLint would make your life much easier (for example, VSCode supports TypeScript out of the box and has this nice ESLint plugin), but you could also use the following commands:
# run tests
npm test
# check code coverage
npm run coverage
# run linter
npm run lint
# run type checker
npm run typecheck
Recommend
-
9
How to Create a Free PDF Document using HTML & CSS in ASP.NET Core 1st April 2021 ...
-
9
We want our products to leave a good impression on first-time users. So whenever we build something, we make it possible first, then ask if it can be a little unique. And the glowing loader is one of those attempts. Here is Hieu's ori...
-
14
HTML Tags: the main tags to create your HTML page #beginners Jun 2...
-
6
-
24
How To Create a Shopping Cart UI Using HTML & CSS?Step by step tutorialThe shopping cart page is designed to allow buyers to see all the products they have added to the shopping cart. It c...
-
7
...
-
13
...
-
8
...
-
7
loreanvictor/quel main
-
10
loreanvictor/packen main
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK