README
createElement
What?
Forgive the unoriginal module name, but this 'script is here to encourage you to use the browser native document
interface,
rather than any attempt to brand itself.
A few principles here:
- (0) Save space, save bandwidth, save parse time.
For writing out HTML with attached events, the DOM API is so full-featured there is no need for a template DSL or front end framework.
Take responsibility for every kilobyte you ship to your users.
The lib
contains a handful of functions and is measured in bytes (~492 bytes with gzip/minify) rather than kilobytes.
- (1) There's no need for document query selectors.
Avoid the class of errors caused by over or under-binding events, the need to match class/id/tag, or waiting for your elements to appear on the document. Leave classNames for styling.
Bind events to elements created in memory, before they are attached to the document. Attach/append to the document when you are ready to give an interactive element to the user, not to query or store intermediate application states.
- (2) Components are still the way to go.
This is a rejection of front end frameworks, diff algorithms, even jQuery, but it is not a rejection of component-based view architecture.
Even using the document
API, you can still compose your views by a hierarchy of components.
Related Information
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
http://youmightnotneedjquery.com/
Similar to: https://github.com/hyperhype/hyperscript + https://github.com/ohanhi/hyperscript-helpers
Import
// CJS module style
const createElement = require('nominal-create-element');
const render = createElement.render;
const nominate = createElement.nominate;
// ESM style, also available in TypeScript
import { createElement, nominate, render } from 'nominal-create-element/createElement.esm';
API
Although there is only one source file of 100 lines, here is the API:
function createElement(tag: string, ...body: body_t[]): HTMLElement;
function createElement(tag: string, className: string, ...body: body_t[]): HTMLElement;
function createElement(tag: string, attributes: { [key: string]: any|(() => void) }, ...body: body_t[]): HTMLElement;
You'll mostly use a partial binding of the createElement
function with the tag already provided, via the
nominate(string)
function.
type nominal_creator_t = {
(...body: body_t[]): HTMLElement;
(className: string, ...body: body_t[]): HTMLElement;
(attributes: { [key: string]: any|(() => void) }, ...body: body_t[]): HTMLElement;
};
function nominate(tag: string): nominal_creator_t;
You can also use the provided render
function if you adhere to a certain component interface.
type component_t = {
element: HTMLElement;
template(): HTMLElement;
}
function render(component: component_t): void {
var element = component.element;
element.parentNode.replaceChild(component.template(), element);
}
examples
"Hold the phone, my friend. What about bundlers and typescript
and tsx
and component organization? Take me straight to the enterprisey example."
or, start simple:
const div = nominate('div');
const span = nominate('span');
Create a simple element.
div(); // HTMLDivElement
Create an element with a css class
.
div('my-css-class');
Create an element with a css class
, some body text and additional attributes
.
div({ id: 'my-css-id', class: 'my-css-class' }, 'my body text');
div({}, 'Hello, world'); // no attributes.
Nest elements
div(
span('a', 'Hello'),
span('b', 'World')
);
form(
{ submit: () => { ... /* my handler here */ } }
div(
input({ placeholder: 'username' })
input({ placeholder: 'password' })
div(
input({ type: 'checkbox', checked: '' }),
span({}, 'Remember my username')
)
)
button({ type: 'submit' }, 'Login')
)
A todo list example.
import { nominate, render } from 'nominal-create-element/createElement.esm';
const div = nominate('div');
const li = nominate('li');
const ul = nominate('ul');
const form = nominate('form');
const input = nominate('input');
const h = n => nominate('h' + n);
const button = nominate('button');
/**
*
* @implements component_t
*
*/
class Todo {
constructor() {
/**
* @type {string[]}
*/
this.list = [
'add something to my todo list'
];
/** @type {HTMLElement} */
this.form = null;
/** @type {HTMLElement} */
this.input = null;
/** @type {HTMLElement} */
this.element = null;
}
/**
* @returns {HTMLElement}
*/
template() {
// This is equivalent to the following HTML:
const html = `
<div style="margin: 25px; padding: 15px;">
<h5>To Do List</h5>
<ul>
<li>add something to my todo list</li>
</ul>
<form>
<input placeholder="Enter to submit">
<button type="submit">Add</button>
</form>
</div>
`;
return this.element =
div(
{ style: 'margin: 25px; padding: 15px;' },
h(5)('', 'To Do List'),
ul(this.list.map(item => li('', item))),
this.form = form(
{
submit: (e) => {
e.preventDefault();
this.list.push(this.input.value);
this.input.value = '';
render(this);
}
},
this.input = input({
placeholder: 'Enter to submit'
}),
button({ type: 'submit' }, 'Add')
)
);
}
}
var todo = new Todo();
document.body.appendChild(todo.template());
Test
open test.html
in your browser.
Curios
An HTML
-to-createElement
converter can be found here.
An enterprisey example written in ts/tsx
can be found here.