-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
dom.ts
67 lines (60 loc) · 2.02 KB
/
dom.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { wait } from './async'
import { parseSimpleCssSelector } from './data'
/**
* Creates and keeps an invisible iframe while the given function runs.
* The given function is called when the iframe is loaded and has a body.
* The iframe allows to measure DOM sizes inside itself.
*
* Notice: passing an initial HTML code doesn't work in IE.
*
* Warning for package users:
* This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk.
*/
export async function withIframe<T>(
action: (iframe: HTMLIFrameElement, iWindow: Window) => Promise<T> | T,
initialHtml?: string,
domPollInterval = 50,
): Promise<T> {
const d = document
// document.body can be null while the page is loading
while (!d.body) {
await wait(domPollInterval)
}
const iframe = d.createElement('iframe')
try {
await new Promise((resolve, reject) => {
iframe.onload = resolve
iframe.onerror = reject
const { style } = iframe
style.setProperty('display', 'block', 'important') // Required for browsers to calculate the layout
style.position = 'absolute'
style.top = '0'
style.left = '0'
style.visibility = 'hidden'
if (initialHtml && 'srcdoc' in iframe) {
iframe.srcdoc = initialHtml
} else {
iframe.src = 'about:blank'
}
d.body.appendChild(iframe)
})
while (!iframe.contentWindow?.document.body) {
await wait(domPollInterval)
}
return await action(iframe, iframe.contentWindow)
} finally {
iframe.parentNode?.removeChild(iframe)
}
}
/**
* Creates a DOM element that matches the given selector.
* Only single element selector are supported (without operators like space, +, >, etc).
*/
export function selectorToElement(selector: string): HTMLElement {
const [tag, attributes] = parseSimpleCssSelector(selector)
const element = document.createElement(tag ?? 'div')
for (const name of Object.keys(attributes)) {
element.setAttribute(name, attributes[name].join(' '))
}
return element
}