Skip to content

Instantly share code, notes, and snippets.

@yusukebe
Last active January 4, 2024 07:37
Show Gist options
  • Save yusukebe/c6e347ad91c5f4cc4691ba25be65e151 to your computer and use it in GitHub Desktop.
Save yusukebe/c6e347ad91c5f4cc4691ba25be65e151 to your computer and use it in GitHub Desktop.
interface JSXNode {
tag: keyof HTMLElementTagNameMap
props?: { [key: string]: any }
children?: (JSXNode | string)[]
}
export class Component {
public elements: HTMLElement[] = []
public render(): JSX.Element {
return <></>
}
public update(): void {
const oldElements = [...this.elements]
this.elements = []
const jsx = this.render()
//@ts-expect-error jsx will be JSXNode
const el = this._render(jsx)
this.elements = [el]
const parent = oldElements[0].parentElement as HTMLElement
if (!parent) console.warn('Component needs a parent element to get updated!')
this.elements.forEach((child: HTMLElement) => {
if (parent) parent.insertBefore(child, oldElements[0])
})
oldElements.forEach((child: HTMLElement) => {
if (!this.elements.includes(child)) {
child.remove()
// @ts-ignore
child = null
}
})
}
_render(jsxNode: JSXNode): HTMLElement {
const tagName = typeof jsxNode.tag === 'string' ? jsxNode.tag : 'fragment'
const element: HTMLElement = document.createElement(tagName)
if (jsxNode.props) {
Object.keys(jsxNode.props).forEach((prop) => {
if (prop.startsWith('on') && typeof jsxNode.props![prop] === 'function') {
const eventName: string = prop.toLowerCase().substring(2)
element.addEventListener(eventName, jsxNode.props![prop])
} else {
element.setAttribute(prop, jsxNode.props![prop])
}
})
}
jsxNode.children?.forEach((child) => {
if (typeof child === 'string') {
element.appendChild(document.createTextNode(child))
} else if (typeof child === 'number') {
element.appendChild(document.createTextNode((child as number).toString()))
} else {
element.appendChild(this._render(child))
}
})
return element
}
}
export const render = (c: any, element: HTMLElement) => {
const klass = c.tag
const component = new klass() as Component
component.elements = [element]
const elem = component.render()
element.appendChild(component._render(elem))
component.elements = [element.children[0]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment