The one-liner
el = (tag, props={}, ch=[]) => ch.reduce((e,c) => (e.appendChild(c),e),Object.assign(document.createElement(tag),props))
Usage
el('ul',{classList:['list']},[
el('li',{innerText:'first'}),
el('li',{innerText:'second'}),
])
Bonus: attributes support
for when properties does not exist 'data-example'
el = (tag, props = {}, ch = [], attrs = {}) => ch.reduce((e, c) => (e.appendChild(c), e), Object.entries(attrs).reduce((e, [k, v]) => (e.setAttribute(k, v), e), Object.assign(document.createElement(tag), props)));
Usage (with attributes)
el('ul',{classList:['list']},[
el('li',{innerText:'first'}),
el('li',{innerText:'second'}),
], {'data-example':42}) // ul extra attributes
Bonus: Typing
This JSTyped version allow better warning/autocomplete and accept helpers params that createElement does not (on_, classList_, dataset ...)
/**
* @template {keyof HTMLElementTagNameMap} T
* @param {T} tagName
* @param {Partial<HTMLElementTagNameMap[T]> & {
* on_?:{ [K in keyof HTMLElementEventMap]?: (this: HTMLElementTagNameMap[T], ev: HTMLElementEventMap[K]) => any },
* classList_?:string[],
* dataset_?:Record<string,string|undefined>,
* style_?:Partial<CSSStyleDeclaration>,
* attributes_?:Record<string,string>
* }} props
* @param {(Node|string)[]} children
* @returns {HTMLElementTagNameMap[T]}
*/
export function el(tagName, { on_ = {}, classList_ = [], dataset_, style_ = {}, attributes_, ...props } = {}, children = []) {
const e = Object.assign(document.createElement(tagName), props);
e.append(...children);
for (const [o, ev] of Object.entries(on_)) e.addEventListener(o, /** @type {EventListener} */(ev)); // TODO: event are not correct
for (const d in dataset_) e.dataset[d] = dataset_[d];
for (const c of classList_) e.classList.add(c);
for (const a in attributes_) e.setAttribute(a, attributes_[a]);
for (const s in style_) if (style_[s]) e.style[s] = style_[s];
return e;
}
Usage (typing)
el("input", { style_: { border: 'none' }, on_: { input() { alert(this.value) } } })
Top comments (0)