Last active
April 27, 2021 10:54
-
-
Save lincore81/372c531b9fc97422e2b50e71ec9ef2fb to your computer and use it in GitHub Desktop.
My clumsy method of injecting HTML into an existing web app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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