mirror of https://github.com/n2geoff/uhm
improved tag(h), to work with htm, xhtm
This commit is contained in:
parent
25a4d003e4
commit
d6ae676d43
72
src/tag.js
72
src/tag.js
|
@ -4,27 +4,67 @@
|
||||||
* Generates new DOM element(s) from a tag, attributes
|
* Generates new DOM element(s) from a tag, attributes
|
||||||
*
|
*
|
||||||
* @param {String} tag - tag name
|
* @param {String} tag - tag name
|
||||||
* @param {Object|String|Array} args - attributes, text or array of child elements
|
* @param {Object} props - tag attributes
|
||||||
|
* @param {Object|String|Array} args - text or array of child elements
|
||||||
*
|
*
|
||||||
* @returns {HTMLElement} The created DOM element(s)
|
* @returns {HTMLElement} The created DOM element(s)
|
||||||
*/
|
*/
|
||||||
export default function h(tag, ...args) {
|
export function h(tagName, props, ...children) {
|
||||||
const el = document.createElement(tag);
|
const el = tagName === DocumentFragment ? document.createDocumentFragment() : document.createElement(tagName);
|
||||||
|
const isScalar = (value) => typeof value === 'string' || typeof value === 'number';
|
||||||
|
const booleanAttrs = ['disabled', 'checked', 'selected', 'hidden', 'readonly', 'required', 'open', 'autoplay', 'loop', 'muted'];
|
||||||
|
|
||||||
// support all scalar values as TextNodes
|
// Handle props (object or null)
|
||||||
const isScalar = (value) => ["boolean", "string", "number"].includes(typeof value);
|
if (props != null && typeof props === 'object' && !Array.isArray(props)) {
|
||||||
|
for (const [key, value] of Object.entries(props)) {
|
||||||
for(let i = 0; i < args.length; i++) {
|
if (value == null) continue;
|
||||||
if (isScalar(args[i])) {
|
if (booleanAttrs.includes(key)) {
|
||||||
el.appendChild(document.createTextNode(args[i]));
|
if (value === true) {
|
||||||
} else if (Array.isArray(args[i])) {
|
el.setAttribute(key, '');
|
||||||
el.append(...args[i]);
|
el[key] = true;
|
||||||
} else {
|
} else if (value === false) {
|
||||||
for(const [k,v] of Object.entries(args[i])) {
|
el.removeAttribute(key);
|
||||||
// if not both ways, some attributes do not render
|
el[key] = false;
|
||||||
el.setAttribute(k, v);
|
}
|
||||||
el[k] = v;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (key.startsWith('on') && typeof value === 'function') {
|
||||||
|
el.addEventListener(key.slice(2).toLowerCase(), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key === 'class') {
|
||||||
|
el.className = value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key === 'style' && typeof value === 'object') {
|
||||||
|
Object.assign(el.style, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
el.setAttribute(key, value);
|
||||||
|
if (key in el) {
|
||||||
|
el[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (props != null) {
|
||||||
|
// If props is not an object, treat it as a child
|
||||||
|
children.unshift(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle children
|
||||||
|
for (const child of children) {
|
||||||
|
if (child == null) continue;
|
||||||
|
if (isScalar(child)) {
|
||||||
|
el.appendChild(document.createTextNode(child));
|
||||||
|
} else if (Array.isArray(child)) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
fragment.append(...child.filter(c => c != null));
|
||||||
|
el.appendChild(fragment);
|
||||||
|
} else if (child instanceof Node) {
|
||||||
|
el.appendChild(child);
|
||||||
|
} else if (typeof child === 'boolean') {
|
||||||
|
console.warn(`Boolean child ${child} passed to h() for tag "${tagName}". Booleans are not rendered.`);
|
||||||
|
} else {
|
||||||
|
console.error(`Unsupported child type: ${typeof child} for tag "${tagName}" in h() function`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue