Skip to content

Instantly share code, notes, and snippets.

@lincore81
Last active April 27, 2021 10:54
Show Gist options
  • Save lincore81/372c531b9fc97422e2b50e71ec9ef2fb to your computer and use it in GitHub Desktop.
Save lincore81/372c531b9fc97422e2b50e71ec9ef2fb to your computer and use it in GitHub Desktop.
My clumsy method of injecting HTML into an existing web app
/* USAGE EXAMPLE:
const mountElem1 = document.body;
const nodes = htmlWindowContent(
{ fontSizes
, defaultFontSize
});
const elems = mount(nodes, mountElem);
function htmlWindowContent({fontSizes, defaultFontSize}) {
return m(DIV, {id: ID_TP_CONTENT},
m(DIV, {id: ID_TP_SCROLL_PANE},
m(DIV, {
id: ID_TP_TEXT_WRAPPER,
style: `font-size: ${defaultFontSize}pt`
},
htmlFontSizePicker({fontSizes, defaultFontSize}),
m(DIV, {id: ID_TP_TRANSCRIPT, "data-ref": "content"}) )));
}
*/
import {mkLogger} from './log';
const log = mkLogger('MOUNT');
export type Node = VNode
| HtmlNode // existing DOM HTMLElement
| TextNode;
export type VNode = {
tag: Tag,
attributes: Attribs,
children: Node[]
};
export type HtmlNode = {
element: HTMLElement
};
export type TextNode = string;
export type RootNode = HtmlNode | VNode;
export type Tag = "div"
| "span"
| "hr"
| "img"
| "label"
| "select"
| "option"
| "button"
| "input";
export type Attribs = Readonly<Partial<{
style: {},
id: string,
src: string,
'data-ref': string,
'class': string,
'for': string,
'data-default-font-size': string,
'value': string,
'aria-hidden': 'true' | 'false',
'selected': 'true' | 'false'
}>>;
/**
* Create a virtual node from the given parameters. If tag is an HTMLElement,
* wrap the element in a HtmlNode instead.
*/
export function m(tagOrElement: Tag | HTMLElement,
attributes: Attribs,
...children: Node[]): RootNode
{
if (typeof tagOrElement !== 'string') {
if (tagOrElement instanceof HTMLElement) {
return {element: tagOrElement} as HtmlNode;
} else {
throw new Error(`Invalid tagOrElement passed to 'mount:m': '${tagOrElement}'.`);
}
}
return {
tag: tagOrElement,
attributes,
children
};
}
export type MountedElements = {[key: string]: HTMLElement};
// Create HTMLElements from 'virtual' nodes and append everything
// to the given parent. Return an assoc array of all created elements with an id.
export function mount(what: RootNode, where: HTMLElement): MountedElements {
log.debug(`mount, what:`, what, `where:`, where);
const elems: MountedElements = {};
makeElem(where, what, elems);
return elems;
}
// recursive:
function makeElem(parent: HTMLElement, node: Node, elems: MountedElements) {
if (isTextNode(node)) {
makeText(node as TextNode, parent);
} else if (isHtmlNode(node)) {
parent.appendChild((node as HtmlNode).element);
} else { // must be a vnode
const vnode = node as VNode;
const elem = document.createElement(vnode.tag);
setAttribs(vnode.attributes, elem);
// memorise element if it has an id
if (vnode.attributes.id) {
elems[vnode.attributes.id] = elem;
}
vnode.children.forEach(child => makeElem(elem, child, elems));
parent.appendChild(elem);
}
}
function isTextNode(node: Node) {
return typeof node === `string`;
}
function isHtmlNode(node: Node) {
return node.hasOwnProperty(`element`);
}
function setAttribs(attribs: Attribs, elem: HTMLElement) {
Object
.entries(attribs)
.forEach(([key, value]) =>
{
const strvalue = typeof value === `string`
? value
: JSON.stringify(value);
elem.setAttribute(key, strvalue);
});
}
function makeText(text: string, parent: HTMLElement) {
const node = document.createTextNode(text);
parent.appendChild(node);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment