/
dom.js
177 lines (159 loc) · 5.23 KB
/
dom.js
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import { RE_HTML_ATTRS, XLINK_NS, XLINK_REGEX, SVG_NS } from './../global-variables'
import { isUndefined } from './check'
/**
* Shorter and fast way to select multiple nodes in the DOM
* @param { String } selector - DOM selector
* @param { Object } ctx - DOM node where the targets of our search will is located
* @returns { Object } dom nodes found
*/
export function $$(selector, ctx) {
return Array.prototype.slice.call((ctx || document).querySelectorAll(selector))
}
/**
* Shorter and fast way to select a single node in the DOM
* @param { String } selector - unique dom selector
* @param { Object } ctx - DOM node where the target of our search will is located
* @returns { Object } dom node found
*/
export function $(selector, ctx) {
return (ctx || document).querySelector(selector)
}
/**
* Create a document fragment
* @returns { Object } document fragment
*/
export function createFrag() {
return document.createDocumentFragment()
}
/**
* Create a document text node
* @returns { Object } create a text node to use as placeholder
*/
export function createDOMPlaceholder() {
return document.createTextNode('')
}
/**
* Check if a DOM node is an svg tag or part of an svg
* @param { HTMLElement } el - node we want to test
* @returns {Boolean} true if it's an svg node
*/
export function isSvg(el) {
const owner = el.ownerSVGElement
return !!owner || owner === null
}
/**
* Create a generic DOM node
* @param { String } name - name of the DOM node we want to create
* @param { Boolean } isSvg - true if we need to use an svg node
* @returns { Object } DOM node just created
*/
export function mkEl(name) {
return name === 'svg' ? document.createElementNS(SVG_NS, name) : document.createElement(name)
}
/**
* Set the inner html of any DOM node SVGs included
* @param { Object } container - DOM node where we'll inject new html
* @param { String } html - html to inject
*/
/* istanbul ignore next */
export function setInnerHTML(container, html) {
if (!isUndefined(container.innerHTML))
container.innerHTML = html
// some browsers do not support innerHTML on the SVGs tags
else {
const doc = new DOMParser().parseFromString(html, 'application/xml')
const node = container.ownerDocument.importNode(doc.documentElement, true)
container.appendChild(node)
}
}
/**
* Toggle the visibility of any DOM node
* @param { Object } dom - DOM node we want to hide
* @param { Boolean } show - do we want to show it?
*/
export function toggleVisibility(dom, show) {
dom.style.display = show ? '' : 'none'
dom.hidden = show ? false : true
}
/**
* Remove any DOM attribute from a node
* @param { Object } dom - DOM node we want to update
* @param { String } name - name of the property we want to remove
*/
export function remAttr(dom, name) {
dom.removeAttribute(name)
}
/**
* Convert a style object to a string
* @param { Object } style - style object we need to parse
* @returns { String } resulting css string
* @example
* styleObjectToString({ color: 'red', height: '10px'}) // => 'color: red; height: 10px'
*/
export function styleObjectToString(style) {
return Object.keys(style).reduce((acc, prop) => {
return `${acc} ${prop}: ${style[prop]};`
}, '')
}
/**
* Get the value of any DOM attribute on a node
* @param { Object } dom - DOM node we want to parse
* @param { String } name - name of the attribute we want to get
* @returns { String | undefined } name of the node attribute whether it exists
*/
export function getAttr(dom, name) {
return dom.getAttribute(name)
}
/**
* Set any DOM attribute
* @param { Object } dom - DOM node we want to update
* @param { String } name - name of the property we want to set
* @param { String } val - value of the property we want to set
*/
export function setAttr(dom, name, val) {
const xlink = XLINK_REGEX.exec(name)
if (xlink && xlink[1])
dom.setAttributeNS(XLINK_NS, xlink[1], val)
else
dom.setAttribute(name, val)
}
/**
* Insert safely a tag to fix #1962 #1649
* @param { HTMLElement } root - children container
* @param { HTMLElement } curr - node to insert
* @param { HTMLElement } next - node that should preceed the current node inserted
*/
export function safeInsert(root, curr, next) {
root.insertBefore(curr, next.parentNode && next)
}
/**
* Minimize risk: only zero or one _space_ between attr & value
* @param { String } html - html string we want to parse
* @param { Function } fn - callback function to apply on any attribute found
*/
export function walkAttrs(html, fn) {
if (!html) return
let m
while (m = RE_HTML_ATTRS.exec(html))
fn(m[1].toLowerCase(), m[2] || m[3] || m[4])
}
/**
* Walk down recursively all the children tags starting dom node
* @param { Object } dom - starting node where we will start the recursion
* @param { Function } fn - callback to transform the child node just found
* @param { Object } context - fn can optionally return an object, which is passed to children
*/
export function walkNodes(dom, fn, context) {
if (dom) {
const res = fn(dom, context)
let next
// stop the recursion
if (res === false) return
dom = dom.firstChild
while (dom) {
next = dom.nextSibling
walkNodes(dom, fn, res)
dom = next
}
}
}