/**
* Creates a HTML canvas element.
* @category DOM
*/
export function createCanvasElement(): HTMLCanvasElement {
const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas') as HTMLCanvasElement
canvas.style.display = 'block'
return canvas
}
/**
* Creates an HTML div element.
* @param innerHTML - HTML string to add to the div
* @param id - id of the div
* @param classList - list of classes to add to the div
* @param addToBody - add the div to the body
* @param elementTag - tag of the element to create (default: div)
*
* @category DOM
*/
export function createDiv({innerHTML = '', id, classList, addToBody = true, elementTag = 'div'}: {innerHTML?: string, id?: string, classList?: string[], addToBody?:boolean, elementTag?: T}): HTMLElementTagNameMap[T] {
const elem = document.createElement(elementTag)
if (id) elem.id = id
elem.innerHTML = innerHTML
if (classList) elem.classList.add(...classList)
if (addToBody) document.body.appendChild(elem)
return elem
}
/**
* Creates a HTML image element from a url.
* @param url - url of the image
*
* @category DOM
*/
export async function createImage(url: string): Promise {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => resolve(img)
img.onerror = reject
img.crossOrigin = 'anonymous'
img.decoding = 'sync'
img.src = url
})
}
/**
* Creates a HTML style element with the given styles.
* @param styles - CSS string
* @param root - root element to add the style to (default: head)
*
* @category DOM
*/
export function createStyles(styles: string, root: Element|undefined = document.head) {
const styleSheet = document.createElement('style')
styleSheet.type = 'text/css'
styleSheet.innerText = styles
root?.appendChild(styleSheet)
return styleSheet
}
/**
* Creates a HTML script element from a url.
* @param url - url of the script
* @param root - root element to add the script to (default: head)
*
* @category DOM
*/
export async function createScriptFromURL(url: string, root = document.head): Promise {
return new Promise((resolve, reject) => {
const s = document.createElement('script')
s.setAttribute('src', url)
s.addEventListener('load', ()=>resolve(s))
s.addEventListener('error', reject)
root.appendChild(s)
})
}
/**
* Sets the innerHTML of an element and recreates all script tags so they are executed.
* @param element - element to set the innerHTML of
* @param html - innerHTML string to set
*
* @category DOM
*/
export async function setInnerHTMLWithScripts(element: HTMLElement, html: string) {
element.innerHTML = html
const scripts = element.getElementsByTagName('script')
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i]
const newScript = cloneScriptTag(script)
let err = false
await new Promise(resolve => {
newScript.onload = resolve
newScript.onerror = () => {
err = true
resolve(undefined)
}
})
if (err) continue
script.parentNode?.replaceChild(newScript, script)
}
}
/**
* Clones a script tag.
* @param script - script tag to clone
* @param newScript - optional new script tag to clone into. If not provided a new script tag will be created.
*
* @category DOM
*/
export function cloneScriptTag(script: HTMLScriptElement, newScript?: HTMLScriptElement) {
newScript = newScript ?? document.createElement('script')
newScript.type = script.type || 'text/javascript'
newScript.text = script.text
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let j = 0; j < script.attributes.length; j++) {
const attr = script.attributes[ j ]
newScript.setAttribute(attr.name, attr.value)
}
return newScript
}