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
|
||||
*
|
||||
* @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)
|
||||
*/
|
||||
export default function h(tag, ...args) {
|
||||
const el = document.createElement(tag);
|
||||
export function h(tagName, props, ...children) {
|
||||
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
|
||||
const isScalar = (value) => ["boolean", "string", "number"].includes(typeof value);
|
||||
|
||||
for(let i = 0; i < args.length; i++) {
|
||||
if (isScalar(args[i])) {
|
||||
el.appendChild(document.createTextNode(args[i]));
|
||||
} else if (Array.isArray(args[i])) {
|
||||
el.append(...args[i]);
|
||||
} else {
|
||||
for(const [k,v] of Object.entries(args[i])) {
|
||||
// if not both ways, some attributes do not render
|
||||
el.setAttribute(k, v);
|
||||
el[k] = v;
|
||||
// Handle props (object or null)
|
||||
if (props != null && typeof props === 'object' && !Array.isArray(props)) {
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (value == null) continue;
|
||||
if (booleanAttrs.includes(key)) {
|
||||
if (value === true) {
|
||||
el.setAttribute(key, '');
|
||||
el[key] = true;
|
||||
} else if (value === false) {
|
||||
el.removeAttribute(key);
|
||||
el[key] = false;
|
||||
}
|
||||
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