Skip to content

Instantly share code, notes, and snippets.

@nyteshade
Last active April 21, 2024 22:58
Show Gist options
  • Save nyteshade/31b3a6b3a84fb4f8304b0c685bac9616 to your computer and use it in GitHub Desktop.
Save nyteshade/31b3a6b3a84fb4f8304b0c685bac9616 to your computer and use it in GitHub Desktop.
(function() {
const HTML = new Proxy(
class HTML {
static create(
name,
content,
style = {},
attributes = {},
webComponentName = undefined,
useDocument = undefined,
children = []
) {
const params = [
'name', 'style', 'attributes', 'webComponentName', 'content',
'useDocument', 'children',
];
let _opts = (
// Check to see if we have individual parameters or a config object
// they would be in content and content must be a non null object type
// it must also contain any of the parameters by name.
(typeof content === 'object' && content) &&
params.some(param => Reflect.has(content ?? {}, param))
// we have a config object; what was passed to the content parameter is it
? content
// we have individual parameters, make a config object from them
: {
style, attributes, content: (Array.isArray(content) ? undefined : content), webComponentName, useDocument, children: Array.isArray(content) ? content : children
}
);
// Validate each parameter is in a usable state for going forward with
// defaults for unsuplied values
Object.assign(_opts, {
style: _opts.style ?? {},
attributes: _opts.attributes ?? {},
content: _opts.content,
webComponentName: _opts.webComponentName,
useDocument: _opts.useDocument ?? top.document,
children: _opts.children ?? []
})
const doc = _opts.useDocument;
const options = _opts.webComponentName ? { is: _opts.webComponentName } : undefined;
const element = doc.createElement(name, options);
for (let [key, value] of Object.entries(_opts.attributes ?? {})) {
const attr = doc.createAttribute(key);
attr.value = value;
element.setAttributeNode(attr);
}
for (let [key, value] of Object.entries(_opts.style ?? {})) {
element.style[key] = value;
}
// avoid this for non-text values
if (_opts.content) {
element.append(doc.createTextNode(_opts.content));
}
for (const child of _opts.children) {
element.append(child);
}
return element;
}
},
{
get(target, property, receiver) {
if (typeof property === 'string' && property !== 'create') {
return HTML.create.bind(HTML, property);
}
return Reflect.get(target, property, receiver);
}
}
);
globalThis['HTML'] = HTML;
})()
@nyteshade
Copy link
Author

This allows a short hand creation of HTML createElement objects with little markup such as

HTML.span('hello') // <span>hello</span>
HTML.span('hello', { fontWeight: 'bold' }) // <span style="font-weight: bold;">hello</span>
HTML.div({ children: [
  HTML.span('hello'),
] }) // <div><span>hello</span></div>

Note that HTML.<tag-name>(...) is possible through the use of Proxy and Function.bind(). It actually does the following:

HTML.create('tag-name', content, style, attributes, webComponentName, useDocument, children)
HTML.create('tag-name', { content, style, attributes, webComponentName, useDocument, children })

Defaults are applied as expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment