UNPKG

6.3 kBJavaScriptView Raw
1/* eslint-env browser */
2
3/**
4 * Utility module to work with the DOM.
5 *
6 * @module dom
7 */
8
9import * as pair from './pair.js'
10import * as map from './map.js'
11
12/* c8 ignore start */
13/**
14 * @type {Document}
15 */
16export const doc = /** @type {Document} */ (typeof document !== 'undefined' ? document : {})
17
18/**
19 * @param {string} name
20 * @return {HTMLElement}
21 */
22export const createElement = name => doc.createElement(name)
23
24/**
25 * @return {DocumentFragment}
26 */
27export const createDocumentFragment = () => doc.createDocumentFragment()
28
29/**
30 * @param {string} text
31 * @return {Text}
32 */
33export const createTextNode = text => doc.createTextNode(text)
34
35export const domParser = /** @type {DOMParser} */ (typeof DOMParser !== 'undefined' ? new DOMParser() : null)
36
37/**
38 * @param {HTMLElement} el
39 * @param {string} name
40 * @param {Object} opts
41 */
42export const emitCustomEvent = (el, name, opts) => el.dispatchEvent(new CustomEvent(name, opts))
43
44/**
45 * @param {Element} el
46 * @param {Array<pair.Pair<string,string|boolean>>} attrs Array of key-value pairs
47 * @return {Element}
48 */
49export const setAttributes = (el, attrs) => {
50 pair.forEach(attrs, (key, value) => {
51 if (value === false) {
52 el.removeAttribute(key)
53 } else if (value === true) {
54 el.setAttribute(key, '')
55 } else {
56 // @ts-ignore
57 el.setAttribute(key, value)
58 }
59 })
60 return el
61}
62
63/**
64 * @param {Element} el
65 * @param {Map<string, string>} attrs Array of key-value pairs
66 * @return {Element}
67 */
68export const setAttributesMap = (el, attrs) => {
69 attrs.forEach((value, key) => { el.setAttribute(key, value) })
70 return el
71}
72
73/**
74 * @param {Array<Node>|HTMLCollection} children
75 * @return {DocumentFragment}
76 */
77export const fragment = children => {
78 const fragment = createDocumentFragment()
79 for (let i = 0; i < children.length; i++) {
80 appendChild(fragment, children[i])
81 }
82 return fragment
83}
84
85/**
86 * @param {Element} parent
87 * @param {Array<Node>} nodes
88 * @return {Element}
89 */
90export const append = (parent, nodes) => {
91 appendChild(parent, fragment(nodes))
92 return parent
93}
94
95/**
96 * @param {HTMLElement} el
97 */
98export const remove = el => el.remove()
99
100/**
101 * @param {EventTarget} el
102 * @param {string} name
103 * @param {EventListener} f
104 */
105export const addEventListener = (el, name, f) => el.addEventListener(name, f)
106
107/**
108 * @param {EventTarget} el
109 * @param {string} name
110 * @param {EventListener} f
111 */
112export const removeEventListener = (el, name, f) => el.removeEventListener(name, f)
113
114/**
115 * @param {Node} node
116 * @param {Array<pair.Pair<string,EventListener>>} listeners
117 * @return {Node}
118 */
119export const addEventListeners = (node, listeners) => {
120 pair.forEach(listeners, (name, f) => addEventListener(node, name, f))
121 return node
122}
123
124/**
125 * @param {Node} node
126 * @param {Array<pair.Pair<string,EventListener>>} listeners
127 * @return {Node}
128 */
129export const removeEventListeners = (node, listeners) => {
130 pair.forEach(listeners, (name, f) => removeEventListener(node, name, f))
131 return node
132}
133
134/**
135 * @param {string} name
136 * @param {Array<pair.Pair<string,string>|pair.Pair<string,boolean>>} attrs Array of key-value pairs
137 * @param {Array<Node>} children
138 * @return {Element}
139 */
140export const element = (name, attrs = [], children = []) =>
141 append(setAttributes(createElement(name), attrs), children)
142
143/**
144 * @param {number} width
145 * @param {number} height
146 */
147export const canvas = (width, height) => {
148 const c = /** @type {HTMLCanvasElement} */ (createElement('canvas'))
149 c.height = height
150 c.width = width
151 return c
152}
153
154/**
155 * @param {string} t
156 * @return {Text}
157 */
158export const text = createTextNode
159
160/**
161 * @param {pair.Pair<string,string>} pair
162 */
163export const pairToStyleString = pair => `${pair.left}:${pair.right};`
164
165/**
166 * @param {Array<pair.Pair<string,string>>} pairs
167 * @return {string}
168 */
169export const pairsToStyleString = pairs => pairs.map(pairToStyleString).join('')
170
171/**
172 * @param {Map<string,string>} m
173 * @return {string}
174 */
175export const mapToStyleString = m => map.map(m, (value, key) => `${key}:${value};`).join('')
176
177/**
178 * @todo should always query on a dom element
179 *
180 * @param {HTMLElement|ShadowRoot} el
181 * @param {string} query
182 * @return {HTMLElement | null}
183 */
184export const querySelector = (el, query) => el.querySelector(query)
185
186/**
187 * @param {HTMLElement|ShadowRoot} el
188 * @param {string} query
189 * @return {NodeListOf<HTMLElement>}
190 */
191export const querySelectorAll = (el, query) => el.querySelectorAll(query)
192
193/**
194 * @param {string} id
195 * @return {HTMLElement}
196 */
197export const getElementById = id => /** @type {HTMLElement} */ (doc.getElementById(id))
198
199/**
200 * @param {string} html
201 * @return {HTMLElement}
202 */
203const _parse = html => domParser.parseFromString(`<html><body>${html}</body></html>`, 'text/html').body
204
205/**
206 * @param {string} html
207 * @return {DocumentFragment}
208 */
209export const parseFragment = html => fragment(/** @type {any} */ (_parse(html).childNodes))
210
211/**
212 * @param {string} html
213 * @return {HTMLElement}
214 */
215export const parseElement = html => /** @type HTMLElement */ (_parse(html).firstElementChild)
216
217/**
218 * @param {HTMLElement} oldEl
219 * @param {HTMLElement|DocumentFragment} newEl
220 */
221export const replaceWith = (oldEl, newEl) => oldEl.replaceWith(newEl)
222
223/**
224 * @param {HTMLElement} parent
225 * @param {HTMLElement} el
226 * @param {Node|null} ref
227 * @return {HTMLElement}
228 */
229export const insertBefore = (parent, el, ref) => parent.insertBefore(el, ref)
230
231/**
232 * @param {Node} parent
233 * @param {Node} child
234 * @return {Node}
235 */
236export const appendChild = (parent, child) => parent.appendChild(child)
237
238export const ELEMENT_NODE = doc.ELEMENT_NODE
239export const TEXT_NODE = doc.TEXT_NODE
240export const CDATA_SECTION_NODE = doc.CDATA_SECTION_NODE
241export const COMMENT_NODE = doc.COMMENT_NODE
242export const DOCUMENT_NODE = doc.DOCUMENT_NODE
243export const DOCUMENT_TYPE_NODE = doc.DOCUMENT_TYPE_NODE
244export const DOCUMENT_FRAGMENT_NODE = doc.DOCUMENT_FRAGMENT_NODE
245
246/**
247 * @param {any} node
248 * @param {number} type
249 */
250export const checkNodeType = (node, type) => node.nodeType === type
251
252/**
253 * @param {Node} parent
254 * @param {HTMLElement} child
255 */
256export const isParentOf = (parent, child) => {
257 let p = child.parentNode
258 while (p && p !== parent) {
259 p = p.parentNode
260 }
261 return p === parent
262}
263/* c8 ignore stop */