Easily create HTML templates in pure JavaScript:
import { h } from '@prsd/aitch';
const nameInput = h`<input type="text" name="name">`;
const onclick = (e) => {
e.preventDefault();
const name = nameInput.value;
// process form data...
};
const form = h`
<form>
Name ${nameInput}
<button type="submit" ${{ onclick }}>
Submit
</button>
</form>
`;
document.body.append(form);
It's just template literals and a tag function so that we can assign values other than strings (i.e. event handlers) from within the template itself.
JavaScript needs an HTML templating language that's:
- Low maintenance
- This package does not introduce a new language for creating HTML. As such, there's very little to maintain outside of bugs, breaking changes introduced by JavaScript or HTML (which is very rare).
- Focused
- It's designed to build HTML templates in JavaScript. State management, routing, etc. is not within the scope of this package.
- Lightweight
- The tag function,
h
, is ~1.4kB.
- The tag function,
All JavaScript packages are required by law to make claims about how fast they
are. aitch
's top speed is 25 salmon per second (sm/s). aitch
updates the DOM
with the grace of a drowning seagull.
npm install @prsd/aitch
- h()
- Create HTML in JavaScript.
- repeat()
- Create repeating HTML elements.
- style()
- Create CSS styles in JavaScript.
Create HTML in JavaScript.
If you use VS Code, you could add HTML syntax highlighting with a plugin like es6-string-html.
import { h } from '@prsd/aitch';
// This will create a single input node.
const input = h`<input type="text" name="firstName">`;
// A fragment is created automatically if there's more than one root node.
const formBody = h`
First name: <input type="text" name="firstName">
Last name: <input type="text" name="lastName">
<button>Submit</button>
`;
// You can compose templates from other nodes.
const firstNameInput = h`<input type="text" name="firstName">`;
const ageInput = h`<input type="number" name="age">`;
const submitBtn = h`<button type="submit">Submit</button>`;
const formBody = h`
First name: ${firstNameInput}
Age: ${ageInput}
${submitBtn}
`;
const form = h`<form>${formBody}</form>`;
// You can interpolate strings, numbers, and booleans. Numbers and booleans are
// converted into strings.
const isHidden = () => true;
const ariaLevel = 5;
const ariaDescription = 'A container that displays...';
const div = h`
<div
aria-level="${ariaLevel}"
aria-hidden="${isHidden()}"
aria-description="${ariaDescription}"
>
Lorem ipsum...
</div>
`;
// You can interpolate more complex values like event handlers with the use of
// a plain object.
const firstNameInput = h`<input type="text" name="firstName">`;
const ageInput = h`<input type="number" name="age">`;
const submitBtn = h`<button type="submit">Submit</button>`;
const onsubmit = (e) => {
e.preventDefault();
/* Does something... */
};
const form = h`
<form ${{ onsubmit }}>
First name: ${firstNameInput}
Age: ${ageInput}
${submitBtn}
</form>
`;
// You can also interpolate inline styles, data attributes, and obtain a
// reference to the node from within the template using a plain object too.
// Please note that:
// (1) All inline styles declared within the "style" must be in camelCase.
// (2) All data attributes declared within the "dataset" property must be in
// camelCase.
// - E.g. dataset: { customData: '123' } = 'data-custom-data="123"'
// (3) "$ref" accepts a callback that receives a reference to the node. You can
// use this, for example, to obtain a reference of the node, perform more
// complex tasks with the node, etc.
// (4) All other properties in an object are assigned based on:
// - (a) If the following is true "property in node", then the property is
// assigned like this: "node[property] = value"
// This is how event handlers are assigned.
// - (b) If (a) is not true, then the property is assigned like this:
// "node.setAttribute(property, value)"
let btn = null; // Gets assigned in using "$ref" below.
const form = h`
<form>
Email: <input type="text">
<button ${{
onclick: (e) => e.preventDefault(),
style: {
padding: '10px',
fontFamily: '"Goudy Bookletter 1911", sans-serif',
},
dataset: {
test: 'submitBtn',
customData: '123',
},
$ref: (el) => (btn = el),
}}>
Submit
</button>
</form>
`;
Create repeating HTML elements.
import { h, repeat } from '@prsd/aitch';
const peopleContainer = document.querySelector('#peopleContainer');
const people = [
{
id: 'A-01',
name: 'John',
},
{
id: 'Q-22',
name: 'Michael',
},
];
const renderPeople = () => {
repeat({
// Required: Container that houses the items.
container: peopleContainer,
// Required: Items to render. Supported types:
// (1) Array
// (2) Set
// (3) Map
// (4) Plain object.
items: people,
// Required: Callback that generates the element node for each item.
element: ({ key, item: { id, name }, index }) => {
return h`<p>${id}<br>${name}</p>`;
},
// Optional: Callback that is activated on each rendered element. This can
// be used to perform more complex tasks with each rendered element.
ref: ({ key, item, index, element }) => {},
// Optional (but recommended to set!): The value returned by this callback
// will be used to uniquely identify each element node.
//
// If this is not set, then the following will be used as keys:
// (1) Arrays : array indices
// (2) Sets : order of properties (effectively like array indices)
// (3) Maps : map keys
// (4) Objects : object keys
//
// The value returned by this callback must be unique!
// If (1) or (2) is used, then we highly recommended setting this callback
// as array and/or set indices do not make good keys.
key: ({ key, item, index }) => item.id,
// Optional: Name of the attribute for the key that would be applied on
// each element. If not set, the default is "data-h-repeat-key".
keyName: 'data-key',
});
};
// Render the list of people.
renderPeople();
// Some event happens and the "people" array is updated.
people.splice(0, 1);
people.push({ id: 'X-30', name: 'Josephine' });
// Call this again to update the DOM.
renderPeople();
Create CSS styles in JavaScript.
import { style } from '@prsd/aitch';
const selector = 'div:hover';
const style = document.createElement('style');
style.innerHTML = style({
p: {
fontSize: '10px',
fontWeight: 'bold',
},
'p > button': {
color: 'blue',
},
[selector]: {
color: 'yellow',
},
[`
p > button,
div > span
`]: {
display: 'absolute',
},
'@media (height: 360px)': {
div: {
fontSize: '10px',
},
p: {
display: 'flex',
flexDirection: 'column',
fontSize: '10px',
},
},
'@keyframes slideIn': {
from: {
transform: 'translateX(0%)',
},
to: {
transform: 'translateX(100%)',
},
},
});
document.head.appendChild(style);