Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/dom/dom.js
4184 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for manipulating the browser's Document Object Model
9
* Inspiration taken *heavily* from mochikit (http://mochikit.com/).
10
*
11
* You can use {@link goog.dom.DomHelper} to create new dom helpers that refer
12
* to a different document object. This is useful if you are working with
13
* frames or multiple windows.
14
*
15
* @suppress {strictMissingProperties}
16
*/
17
18
19
// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem
20
// is that getTextContent should mimic the DOM3 textContent. We should add a
21
// getInnerText (or getText) which tries to return the visible text, innerText.
22
23
24
goog.provide('goog.dom');
25
goog.provide('goog.dom.Appendable');
26
goog.provide('goog.dom.DomHelper');
27
28
goog.require('goog.array');
29
goog.require('goog.asserts');
30
goog.require('goog.asserts.dom');
31
goog.require('goog.dom.BrowserFeature');
32
goog.require('goog.dom.NodeType');
33
goog.require('goog.dom.TagName');
34
goog.require('goog.dom.safe');
35
goog.require('goog.html.SafeHtml');
36
goog.require('goog.html.uncheckedconversions');
37
goog.require('goog.math.Coordinate');
38
goog.require('goog.math.Size');
39
goog.require('goog.object');
40
goog.require('goog.string');
41
goog.require('goog.string.Const');
42
goog.require('goog.string.Unicode');
43
goog.require('goog.userAgent');
44
45
goog.require('goog.utils');
46
47
48
/**
49
* @define {boolean} Whether we know at compile time that the browser is in
50
* quirks mode.
51
*/
52
goog.dom.ASSUME_QUIRKS_MODE = goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);
53
54
55
/**
56
* @define {boolean} Whether we know at compile time that the browser is in
57
* standards compliance mode.
58
*/
59
goog.dom.ASSUME_STANDARDS_MODE =
60
goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);
61
62
63
/**
64
* Whether we know the compatibility mode at compile time.
65
* @type {boolean}
66
* @private
67
*/
68
goog.dom.COMPAT_MODE_KNOWN_ =
69
goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
70
71
72
/**
73
* Gets the DomHelper object for the document where the element resides.
74
* @param {(Node|Window)=} opt_element If present, gets the DomHelper for this
75
* element.
76
* @return {!goog.dom.DomHelper} The DomHelper.
77
*/
78
goog.dom.getDomHelper = function(opt_element) {
79
'use strict';
80
return opt_element ?
81
new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) :
82
(goog.dom.defaultDomHelper_ ||
83
(goog.dom.defaultDomHelper_ = new goog.dom.DomHelper()));
84
};
85
86
87
/**
88
* Cached default DOM helper.
89
* @type {!goog.dom.DomHelper|undefined}
90
* @private
91
*/
92
goog.dom.defaultDomHelper_;
93
94
95
/**
96
* Gets the document object being used by the dom library.
97
* @return {!Document} Document object.
98
*/
99
goog.dom.getDocument = function() {
100
'use strict';
101
return document;
102
};
103
104
105
/**
106
* Gets an element from the current document by element id.
107
*
108
* If an Element is passed in, it is returned.
109
*
110
* @param {string|Element} element Element ID or a DOM node.
111
* @return {Element} The element with the given ID, or the node passed in.
112
*/
113
goog.dom.getElement = function(element) {
114
'use strict';
115
return goog.dom.getElementHelper_(document, element);
116
};
117
118
119
/**
120
* Gets an HTML element from the current document by element id.
121
*
122
* @param {string} id
123
* @return {?HTMLElement} The element with the given ID or null if no such
124
* element exists.
125
*/
126
goog.dom.getHTMLElement = function(id) {
127
'use strict'
128
const element = goog.dom.getElement(id);
129
if (!element) {
130
return null;
131
}
132
return goog.asserts.dom.assertIsHtmlElement(element);
133
};
134
135
136
/**
137
* Gets an element by id from the given document (if present).
138
* If an element is given, it is returned.
139
* @param {!Document} doc
140
* @param {string|Element} element Element ID or a DOM node.
141
* @return {Element} The resulting element.
142
* @private
143
*/
144
goog.dom.getElementHelper_ = function(doc, element) {
145
'use strict';
146
return typeof element === 'string' ? doc.getElementById(element) : element;
147
};
148
149
150
/**
151
* Gets an element by id, asserting that the element is found.
152
*
153
* This is used when an element is expected to exist, and should fail with
154
* an assertion error if it does not (if assertions are enabled).
155
*
156
* @param {string} id Element ID.
157
* @return {!Element} The element with the given ID, if it exists.
158
*/
159
goog.dom.getRequiredElement = function(id) {
160
'use strict';
161
return goog.dom.getRequiredElementHelper_(document, id);
162
};
163
164
165
/**
166
* Gets an HTML element by id, asserting that the element is found.
167
*
168
* This is used when an element is expected to exist, and should fail with
169
* an assertion error if it does not (if assertions are enabled).
170
*
171
* @param {string} id Element ID.
172
* @return {!HTMLElement} The element with the given ID, if it exists.
173
*/
174
goog.dom.getRequiredHTMLElement = function(id) {
175
'use strict'
176
return goog.asserts.dom.assertIsHtmlElement(
177
goog.dom.getRequiredElementHelper_(document, id));
178
};
179
180
181
/**
182
* Helper function for getRequiredElementHelper functions, both static and
183
* on DomHelper. Asserts the element with the given id exists.
184
* @param {!Document} doc
185
* @param {string} id
186
* @return {!Element} The element with the given ID, if it exists.
187
* @private
188
*/
189
goog.dom.getRequiredElementHelper_ = function(doc, id) {
190
'use strict';
191
// To prevent users passing in Elements as is permitted in getElement().
192
goog.asserts.assertString(id);
193
var element = goog.dom.getElementHelper_(doc, id);
194
return goog.asserts.assert(element, 'No element found with id: ' + id);
195
};
196
197
198
/**
199
* Alias for getElement.
200
* @param {string|Element} element Element ID or a DOM node.
201
* @return {Element} The element with the given ID, or the node passed in.
202
* @deprecated Use {@link goog.dom.getElement} instead.
203
*/
204
goog.dom.$ = goog.dom.getElement;
205
206
207
/**
208
* Gets elements by tag name.
209
* @param {!goog.dom.TagName<T>} tagName
210
* @param {(!Document|!Element)=} opt_parent Parent element or document where to
211
* look for elements. Defaults to document.
212
* @return {!NodeList<R>} List of elements. The members of the list are
213
* {!Element} if tagName is not a member of goog.dom.TagName or more
214
* specific types if it is (e.g. {!HTMLAnchorElement} for
215
* goog.dom.TagName.A).
216
* @template T
217
* @template R := cond(isUnknown(T), 'Element', T) =:
218
*/
219
goog.dom.getElementsByTagName = function(tagName, opt_parent) {
220
'use strict';
221
var parent = opt_parent || document;
222
return parent.getElementsByTagName(String(tagName));
223
};
224
225
226
/**
227
* Looks up elements by both tag and class name, using browser native functions
228
* (`querySelectorAll`, `getElementsByTagName` or
229
* `getElementsByClassName`) where possible. This function
230
* is a useful, if limited, way of collecting a list of DOM elements
231
* with certain characteristics. `querySelectorAll` offers a
232
* more powerful and general solution which allows matching on CSS3
233
* selector expressions.
234
*
235
* Note that tag names are case sensitive in the SVG namespace, and this
236
* function converts opt_tag to uppercase for comparisons. For queries in the
237
* SVG namespace you should use querySelector or querySelectorAll instead.
238
* https://bugzilla.mozilla.org/show_bug.cgi?id=963870
239
* https://bugs.webkit.org/show_bug.cgi?id=83438
240
*
241
* @see {https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll}
242
*
243
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
244
* @param {?string=} opt_class Optional class name.
245
* @param {(Document|Element)=} opt_el Optional element to look in.
246
* @return {!IArrayLike<R>} Array-like list of elements (only a length property
247
* and numerical indices are guaranteed to exist). The members of the array
248
* are {!Element} if opt_tag is not a member of goog.dom.TagName or more
249
* specific types if it is (e.g. {!HTMLAnchorElement} for
250
* goog.dom.TagName.A).
251
* @template T
252
* @template R := cond(isUnknown(T), 'Element', T) =:
253
*/
254
goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
255
'use strict';
256
return goog.dom.getElementsByTagNameAndClass_(
257
document, opt_tag, opt_class, opt_el);
258
};
259
260
261
/**
262
* Gets the first element matching the tag and the class.
263
*
264
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
265
* @param {?string=} opt_class Optional class name.
266
* @param {(Document|Element)=} opt_el Optional element to look in.
267
* @return {?R} Reference to a DOM node. The return type is {?Element} if
268
* tagName is a string or a more specific type if it is a member of
269
* goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
270
* @template T
271
* @template R := cond(isUnknown(T), 'Element', T) =:
272
*/
273
goog.dom.getElementByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
274
'use strict';
275
return goog.dom.getElementByTagNameAndClass_(
276
document, opt_tag, opt_class, opt_el);
277
};
278
279
280
/**
281
* Returns a static, array-like list of the elements with the provided
282
* className.
283
*
284
* @param {string} className the name of the class to look for.
285
* @param {(Document|Element)=} opt_el Optional element to look in.
286
* @return {!IArrayLike<!Element>} The items found with the class name provided.
287
*/
288
goog.dom.getElementsByClass = function(className, opt_el) {
289
'use strict';
290
var parent = opt_el || document;
291
if (goog.dom.canUseQuerySelector_(parent)) {
292
return parent.querySelectorAll('.' + className);
293
}
294
return goog.dom.getElementsByTagNameAndClass_(
295
document, '*', className, opt_el);
296
};
297
298
299
/**
300
* Returns the first element with the provided className.
301
*
302
* @param {string} className the name of the class to look for.
303
* @param {Element|Document=} opt_el Optional element to look in.
304
* @return {Element} The first item with the class name provided.
305
*/
306
goog.dom.getElementByClass = function(className, opt_el) {
307
'use strict';
308
var parent = opt_el || document;
309
var retVal = null;
310
if (parent.getElementsByClassName) {
311
retVal = parent.getElementsByClassName(className)[0];
312
} else {
313
retVal =
314
goog.dom.getElementByTagNameAndClass_(document, '*', className, opt_el);
315
}
316
return retVal || null;
317
};
318
319
320
/**
321
* Returns the first element with the provided className and asserts that it is
322
* an HTML element.
323
*
324
* @param {string} className the name of the class to look for.
325
* @param {!Element|!Document=} opt_parent Optional element to look in.
326
* @return {?HTMLElement} The first item with the class name provided.
327
*/
328
goog.dom.getHTMLElementByClass = function(className, opt_parent) {
329
'use strict'
330
const element = goog.dom.getElementByClass(className, opt_parent);
331
if (!element) {
332
return null;
333
}
334
return goog.asserts.dom.assertIsHtmlElement(element);
335
};
336
337
338
/**
339
* Ensures an element with the given className exists, and then returns the
340
* first element with the provided className.
341
*
342
* @param {string} className the name of the class to look for.
343
* @param {!Element|!Document=} opt_root Optional element or document to look
344
* in.
345
* @return {!Element} The first item with the class name provided.
346
* @throws {goog.asserts.AssertionError} Thrown if no element is found.
347
*/
348
goog.dom.getRequiredElementByClass = function(className, opt_root) {
349
'use strict';
350
var retValue = goog.dom.getElementByClass(className, opt_root);
351
return goog.asserts.assert(
352
retValue, 'No element found with className: ' + className);
353
};
354
355
356
/**
357
* Ensures an element with the given className exists, and then returns the
358
* first element with the provided className after asserting that it is an
359
* HTML element.
360
*
361
* @param {string} className the name of the class to look for.
362
* @param {!Element|!Document=} opt_parent Optional element or document to look
363
* in.
364
* @return {!HTMLElement} The first item with the class name provided.
365
*/
366
goog.dom.getRequiredHTMLElementByClass = function(className, opt_parent) {
367
'use strict'
368
const retValue = goog.dom.getElementByClass(className, opt_parent);
369
goog.asserts.assert(
370
retValue, 'No HTMLElement found with className: ' + className);
371
return goog.asserts.dom.assertIsHtmlElement(retValue);
372
};
373
374
375
/**
376
* Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and
377
* fast W3C Selectors API.
378
* @param {!(Element|Document)} parent The parent document object.
379
* @return {boolean} whether or not we can use parent.querySelector* APIs.
380
* @private
381
*/
382
goog.dom.canUseQuerySelector_ = function(parent) {
383
'use strict';
384
return !!(parent.querySelectorAll && parent.querySelector);
385
};
386
387
388
/**
389
* Helper for `getElementsByTagNameAndClass`.
390
* @param {!Document} doc The document to get the elements in.
391
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
392
* @param {?string=} opt_class Optional class name.
393
* @param {(Document|Element)=} opt_el Optional element to look in.
394
* @return {!IArrayLike<R>} Array-like list of elements (only a length property
395
* and numerical indices are guaranteed to exist). The members of the array
396
* are {!Element} if opt_tag is not a member of goog.dom.TagName or more
397
* specific types if it is (e.g. {!HTMLAnchorElement} for
398
* goog.dom.TagName.A).
399
* @template T
400
* @template R := cond(isUnknown(T), 'Element', T) =:
401
* @private
402
*/
403
goog.dom.getElementsByTagNameAndClass_ = function(
404
doc, opt_tag, opt_class, opt_el) {
405
'use strict';
406
var parent = opt_el || doc;
407
var tagName =
408
(opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';
409
410
if (goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {
411
var query = tagName + (opt_class ? '.' + opt_class : '');
412
return parent.querySelectorAll(query);
413
}
414
415
// Use the native getElementsByClassName if available, under the assumption
416
// that even when the tag name is specified, there will be fewer elements to
417
// filter through when going by class than by tag name
418
if (opt_class && parent.getElementsByClassName) {
419
var els = parent.getElementsByClassName(opt_class);
420
421
if (tagName) {
422
var arrayLike = {};
423
var len = 0;
424
425
// Filter for specific tags if requested.
426
for (var i = 0, el; el = els[i]; i++) {
427
if (tagName == el.nodeName) {
428
arrayLike[len++] = el;
429
}
430
}
431
arrayLike.length = len;
432
433
return /** @type {!IArrayLike<!Element>} */ (arrayLike);
434
} else {
435
return els;
436
}
437
}
438
439
var els = parent.getElementsByTagName(tagName || '*');
440
441
if (opt_class) {
442
var arrayLike = {};
443
var len = 0;
444
for (var i = 0, el; el = els[i]; i++) {
445
var className = el.className;
446
// Check if className has a split function since SVG className does not.
447
if (typeof className.split == 'function' &&
448
goog.array.contains(className.split(/\s+/), opt_class)) {
449
arrayLike[len++] = el;
450
}
451
}
452
arrayLike.length = len;
453
return /** @type {!IArrayLike<!Element>} */ (arrayLike);
454
} else {
455
return els;
456
}
457
};
458
459
460
/**
461
* Helper for goog.dom.getElementByTagNameAndClass.
462
*
463
* @param {!Document} doc The document to get the elements in.
464
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
465
* @param {?string=} opt_class Optional class name.
466
* @param {(Document|Element)=} opt_el Optional element to look in.
467
* @return {?R} Reference to a DOM node. The return type is {?Element} if
468
* tagName is a string or a more specific type if it is a member of
469
* goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
470
* @template T
471
* @template R := cond(isUnknown(T), 'Element', T) =:
472
* @private
473
*/
474
goog.dom.getElementByTagNameAndClass_ = function(
475
doc, opt_tag, opt_class, opt_el) {
476
'use strict';
477
var parent = opt_el || doc;
478
var tag = (opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';
479
if (goog.dom.canUseQuerySelector_(parent) && (tag || opt_class)) {
480
return parent.querySelector(tag + (opt_class ? '.' + opt_class : ''));
481
}
482
var elements =
483
goog.dom.getElementsByTagNameAndClass_(doc, opt_tag, opt_class, opt_el);
484
return elements[0] || null;
485
};
486
487
488
489
/**
490
* Alias for `getElementsByTagNameAndClass`.
491
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
492
* @param {?string=} opt_class Optional class name.
493
* @param {Element=} opt_el Optional element to look in.
494
* @return {!IArrayLike<R>} Array-like list of elements (only a length property
495
* and numerical indices are guaranteed to exist). The members of the array
496
* are {!Element} if opt_tag is not a member of goog.dom.TagName or more
497
* specific types if it is (e.g. {!HTMLAnchorElement} for
498
* goog.dom.TagName.A).
499
* @template T
500
* @template R := cond(isUnknown(T), 'Element', T) =:
501
* @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.
502
*/
503
goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;
504
505
506
/**
507
* Sets multiple properties, and sometimes attributes, on an element. Note that
508
* properties are simply object properties on the element instance, while
509
* attributes are visible in the DOM. Many properties map to attributes with the
510
* same names, some with different names, and there are also unmappable cases.
511
*
512
* This method sets properties by default (which means that custom attributes
513
* are not supported). These are the exeptions (some of which is legacy):
514
* - "style": Even though this is an attribute name, it is translated to a
515
* property, "style.cssText". Note that this property sanitizes and formats
516
* its value, unlike the attribute.
517
* - "class": This is an attribute name, it is translated to the "className"
518
* property.
519
* - "for": This is an attribute name, it is translated to the "htmlFor"
520
* property.
521
* - Entries in {@see goog.dom.DIRECT_ATTRIBUTE_MAP_} are set as attributes,
522
* this is probably due to browser quirks.
523
* - "aria-*", "data-*": Always set as attributes, they have no property
524
* counterparts.
525
*
526
* @param {Element} element DOM node to set properties on.
527
* @param {Object} properties Hash of property:value pairs.
528
* Property values can be strings or goog.string.TypedString values (such as
529
* goog.html.SafeUrl).
530
*/
531
goog.dom.setProperties = function(element, properties) {
532
'use strict';
533
goog.object.forEach(properties, function(val, key) {
534
'use strict';
535
if (val && typeof val == 'object' && val.implementsGoogStringTypedString) {
536
val = val.getTypedStringValue();
537
}
538
if (key == 'style') {
539
element.style.cssText = val;
540
} else if (key == 'class') {
541
element.className = val;
542
} else if (key == 'for') {
543
element.htmlFor = val;
544
} else if (goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key)) {
545
element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);
546
} else if (
547
goog.string.startsWith(key, 'aria-') ||
548
goog.string.startsWith(key, 'data-')) {
549
element.setAttribute(key, val);
550
} else {
551
element[key] = val;
552
}
553
});
554
};
555
556
557
/**
558
* Map of attributes that should be set using
559
* element.setAttribute(key, val) instead of element[key] = val. Used
560
* by goog.dom.setProperties.
561
*
562
* @private {!Object<string, string>}
563
* @const
564
*/
565
goog.dom.DIRECT_ATTRIBUTE_MAP_ = {
566
'cellpadding': 'cellPadding',
567
'cellspacing': 'cellSpacing',
568
'colspan': 'colSpan',
569
'frameborder': 'frameBorder',
570
'height': 'height',
571
'maxlength': 'maxLength',
572
'nonce': 'nonce',
573
'role': 'role',
574
'rowspan': 'rowSpan',
575
'type': 'type',
576
'usemap': 'useMap',
577
'valign': 'vAlign',
578
'width': 'width'
579
};
580
581
582
/**
583
* Gets the dimensions of the viewport.
584
*
585
* Gecko Standards mode:
586
* docEl.clientWidth Width of viewport excluding scrollbar.
587
* win.innerWidth Width of viewport including scrollbar.
588
* body.clientWidth Width of body element.
589
*
590
* docEl.clientHeight Height of viewport excluding scrollbar.
591
* win.innerHeight Height of viewport including scrollbar.
592
* body.clientHeight Height of document.
593
*
594
* Gecko Backwards compatible mode:
595
* docEl.clientWidth Width of viewport excluding scrollbar.
596
* win.innerWidth Width of viewport including scrollbar.
597
* body.clientWidth Width of viewport excluding scrollbar.
598
*
599
* docEl.clientHeight Height of document.
600
* win.innerHeight Height of viewport including scrollbar.
601
* body.clientHeight Height of viewport excluding scrollbar.
602
*
603
* IE6/7 Standards mode:
604
* docEl.clientWidth Width of viewport excluding scrollbar.
605
* win.innerWidth Undefined.
606
* body.clientWidth Width of body element.
607
*
608
* docEl.clientHeight Height of viewport excluding scrollbar.
609
* win.innerHeight Undefined.
610
* body.clientHeight Height of document element.
611
*
612
* IE5 + IE6/7 Backwards compatible mode:
613
* docEl.clientWidth 0.
614
* win.innerWidth Undefined.
615
* body.clientWidth Width of viewport excluding scrollbar.
616
*
617
* docEl.clientHeight 0.
618
* win.innerHeight Undefined.
619
* body.clientHeight Height of viewport excluding scrollbar.
620
*
621
* Opera 9 Standards and backwards compatible mode:
622
* docEl.clientWidth Width of viewport excluding scrollbar.
623
* win.innerWidth Width of viewport including scrollbar.
624
* body.clientWidth Width of viewport excluding scrollbar.
625
*
626
* docEl.clientHeight Height of document.
627
* win.innerHeight Height of viewport including scrollbar.
628
* body.clientHeight Height of viewport excluding scrollbar.
629
*
630
* WebKit:
631
* Safari 2
632
* docEl.clientHeight Same as scrollHeight.
633
* docEl.clientWidth Same as innerWidth.
634
* win.innerWidth Width of viewport excluding scrollbar.
635
* win.innerHeight Height of the viewport including scrollbar.
636
* frame.innerHeight Height of the viewport excluding scrollbar.
637
*
638
* Safari 3 (tested in 522)
639
*
640
* docEl.clientWidth Width of viewport excluding scrollbar.
641
* docEl.clientHeight Height of viewport excluding scrollbar in strict mode.
642
* body.clientHeight Height of viewport excluding scrollbar in quirks mode.
643
*
644
* @param {Window=} opt_window Optional window element to test.
645
* @return {!goog.math.Size} Object with values 'width' and 'height'.
646
*/
647
goog.dom.getViewportSize = function(opt_window) {
648
'use strict';
649
// TODO(arv): This should not take an argument
650
return goog.dom.getViewportSize_(opt_window || window);
651
};
652
653
654
/**
655
* Helper for `getViewportSize`.
656
* @param {Window} win The window to get the view port size for.
657
* @return {!goog.math.Size} Object with values 'width' and 'height'.
658
* @private
659
*/
660
goog.dom.getViewportSize_ = function(win) {
661
'use strict';
662
var doc = win.document;
663
var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;
664
return new goog.math.Size(el.clientWidth, el.clientHeight);
665
};
666
667
668
/**
669
* Calculates the height of the document.
670
*
671
* @return {number} The height of the current document.
672
*/
673
goog.dom.getDocumentHeight = function() {
674
'use strict';
675
return goog.dom.getDocumentHeight_(window);
676
};
677
678
/**
679
* Calculates the height of the document of the given window.
680
*
681
* @param {!Window} win The window whose document height to retrieve.
682
* @return {number} The height of the document of the given window.
683
*/
684
goog.dom.getDocumentHeightForWindow = function(win) {
685
'use strict';
686
return goog.dom.getDocumentHeight_(win);
687
};
688
689
/**
690
* Calculates the height of the document of the given window.
691
*
692
* Function code copied from the opensocial gadget api:
693
* gadgets.window.adjustHeight(opt_height)
694
*
695
* @private
696
* @param {!Window} win The window whose document height to retrieve.
697
* @return {number} The height of the document of the given window.
698
*/
699
goog.dom.getDocumentHeight_ = function(win) {
700
'use strict';
701
// NOTE(eae): This method will return the window size rather than the document
702
// size in webkit quirks mode.
703
var doc = win.document;
704
var height = 0;
705
706
if (doc) {
707
// Calculating inner content height is hard and different between
708
// browsers rendering in Strict vs. Quirks mode. We use a combination of
709
// three properties within document.body and document.documentElement:
710
// - scrollHeight
711
// - offsetHeight
712
// - clientHeight
713
// These values differ significantly between browsers and rendering modes.
714
// But there are patterns. It just takes a lot of time and persistence
715
// to figure out.
716
717
var body = doc.body;
718
var docEl = /** @type {!HTMLElement} */ (doc.documentElement);
719
if (!(docEl && body)) {
720
return 0;
721
}
722
723
// Get the height of the viewport
724
var vh = goog.dom.getViewportSize_(win).height;
725
if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {
726
// In Strict mode:
727
// The inner content height is contained in either:
728
// document.documentElement.scrollHeight
729
// document.documentElement.offsetHeight
730
// Based on studying the values output by different browsers,
731
// use the value that's NOT equal to the viewport height found above.
732
height =
733
docEl.scrollHeight != vh ? docEl.scrollHeight : docEl.offsetHeight;
734
} else {
735
// In Quirks mode:
736
// documentElement.clientHeight is equal to documentElement.offsetHeight
737
// except in IE. In most browsers, document.documentElement can be used
738
// to calculate the inner content height.
739
// However, in other browsers (e.g. IE), document.body must be used
740
// instead. How do we know which one to use?
741
// If document.documentElement.clientHeight does NOT equal
742
// document.documentElement.offsetHeight, then use document.body.
743
var sh = docEl.scrollHeight;
744
var oh = docEl.offsetHeight;
745
if (docEl.clientHeight != oh) {
746
sh = body.scrollHeight;
747
oh = body.offsetHeight;
748
}
749
750
// Detect whether the inner content height is bigger or smaller
751
// than the bounding box (viewport). If bigger, take the larger
752
// value. If smaller, take the smaller value.
753
if (sh > vh) {
754
// Content is larger
755
height = sh > oh ? sh : oh;
756
} else {
757
// Content is smaller
758
height = sh < oh ? sh : oh;
759
}
760
}
761
}
762
763
return height;
764
};
765
766
767
/**
768
* Gets the page scroll distance as a coordinate object.
769
*
770
* @param {Window=} opt_window Optional window element to test.
771
* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
772
* @deprecated Use {@link goog.dom.getDocumentScroll} instead.
773
*/
774
goog.dom.getPageScroll = function(opt_window) {
775
'use strict';
776
var win = opt_window || goog.global || window;
777
return goog.dom.getDomHelper(win.document).getDocumentScroll();
778
};
779
780
781
/**
782
* Gets the document scroll distance as a coordinate object.
783
*
784
* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
785
*/
786
goog.dom.getDocumentScroll = function() {
787
'use strict';
788
return goog.dom.getDocumentScroll_(document);
789
};
790
791
792
/**
793
* Helper for `getDocumentScroll`.
794
*
795
* @param {!Document} doc The document to get the scroll for.
796
* @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
797
* @private
798
*/
799
goog.dom.getDocumentScroll_ = function(doc) {
800
'use strict';
801
var el = goog.dom.getDocumentScrollElement_(doc);
802
var win = goog.dom.getWindow_(doc);
803
if (goog.userAgent.IE && win.pageYOffset != el.scrollTop) {
804
// The keyboard on IE10 touch devices shifts the page using the pageYOffset
805
// without modifying scrollTop. For this case, we want the body scroll
806
// offsets.
807
return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);
808
}
809
return new goog.math.Coordinate(
810
win.pageXOffset || el.scrollLeft, win.pageYOffset || el.scrollTop);
811
};
812
813
814
/**
815
* Gets the document scroll element.
816
* @return {!Element} Scrolling element.
817
*/
818
goog.dom.getDocumentScrollElement = function() {
819
'use strict';
820
return goog.dom.getDocumentScrollElement_(document);
821
};
822
823
824
/**
825
* Helper for `getDocumentScrollElement`.
826
* @param {!Document} doc The document to get the scroll element for.
827
* @return {!Element} Scrolling element.
828
* @private
829
*/
830
goog.dom.getDocumentScrollElement_ = function(doc) {
831
'use strict';
832
// Old WebKit needs body.scrollLeft in both quirks mode and strict mode. We
833
// also default to the documentElement if the document does not have a body
834
// (e.g. a SVG document).
835
// Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement to
836
// avoid trying to guess about browser behavior from the UA string.
837
if (doc.scrollingElement) {
838
return doc.scrollingElement;
839
}
840
if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {
841
return doc.documentElement;
842
}
843
return doc.body || doc.documentElement;
844
};
845
846
847
/**
848
* Gets the window object associated with the given document.
849
*
850
* @param {Document=} opt_doc Document object to get window for.
851
* @return {!Window} The window associated with the given document.
852
*/
853
goog.dom.getWindow = function(opt_doc) {
854
'use strict';
855
// TODO(arv): This should not take an argument.
856
return opt_doc ? goog.dom.getWindow_(opt_doc) : window;
857
};
858
859
860
/**
861
* Helper for `getWindow`.
862
*
863
* @param {!Document} doc Document object to get window for.
864
* @return {!Window} The window associated with the given document.
865
* @private
866
*/
867
goog.dom.getWindow_ = function(doc) {
868
'use strict';
869
return /** @type {!Window} */ (doc.parentWindow || doc.defaultView);
870
};
871
872
873
/**
874
* Returns a dom node with a set of attributes. This function accepts varargs
875
* for subsequent nodes to be added. Subsequent nodes will be added to the
876
* first node as childNodes.
877
*
878
* So:
879
* <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),
880
* createDom(goog.dom.TagName.P));</code> would return a div with two child
881
* paragraphs
882
*
883
* This function uses {@link goog.dom.setProperties} to set attributes: the
884
* `opt_attributes` parameter follows the same rules.
885
*
886
* @param {string|!goog.dom.TagName<T>} tagName Tag to create.
887
* @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
888
* of name-value pairs for attributes. If a string, then this is the
889
* className of the new element. If an array, the elements will be joined
890
* together as the className of the new element.
891
* @param {...(Object|string|Array|NodeList|null|undefined)} var_args Further
892
* DOM nodes or strings for text nodes. If one of the var_args is an array
893
* or NodeList, its elements will be added as childNodes instead.
894
* @return {R} Reference to a DOM node. The return type is {!Element} if tagName
895
* is a string or a more specific type if it is a member of
896
* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
897
* @template T
898
* @template R := cond(isUnknown(T), 'Element', T) =:
899
*/
900
goog.dom.createDom = function(tagName, opt_attributes, var_args) {
901
'use strict';
902
return goog.dom.createDom_(document, arguments);
903
};
904
905
906
/**
907
* Helper for `createDom`.
908
* @param {!Document} doc The document to create the DOM in.
909
* @param {!Arguments} args Argument object passed from the callers. See
910
* `goog.dom.createDom` for details.
911
* @return {!Element} Reference to a DOM node.
912
* @private
913
*/
914
goog.dom.createDom_ = function(doc, args) {
915
'use strict';
916
var tagName = String(args[0]);
917
var attributes = args[1];
918
919
var element = goog.dom.createElement_(doc, tagName);
920
921
if (attributes) {
922
if (typeof attributes === 'string') {
923
element.className = attributes;
924
} else if (Array.isArray(attributes)) {
925
element.className = attributes.join(' ');
926
} else {
927
goog.dom.setProperties(element, attributes);
928
}
929
}
930
931
if (args.length > 2) {
932
goog.dom.append_(doc, element, args, 2);
933
}
934
935
return element;
936
};
937
938
939
/**
940
* Appends a node with text or other nodes.
941
* @param {!Document} doc The document to create new nodes in.
942
* @param {!Node} parent The node to append nodes to.
943
* @param {!Arguments} args The values to add. See `goog.dom.append`.
944
* @param {number} startIndex The index of the array to start from.
945
* @private
946
*/
947
goog.dom.append_ = function(doc, parent, args, startIndex) {
948
'use strict';
949
function childHandler(child) {
950
// TODO(user): More coercion, ala MochiKit?
951
if (child) {
952
parent.appendChild(
953
typeof child === 'string' ? doc.createTextNode(child) : child);
954
}
955
}
956
957
for (var i = startIndex; i < args.length; i++) {
958
var arg = args[i];
959
// TODO(attila): Fix isArrayLike to return false for a text node.
960
if (goog.utils.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
961
// If the argument is a node list, not a real array, use a clone,
962
// because forEach can't be used to mutate a NodeList.
963
goog.array.forEach(
964
goog.dom.isNodeList(arg) ? goog.array.toArray(arg) : arg,
965
childHandler);
966
} else {
967
childHandler(arg);
968
}
969
}
970
};
971
972
973
/**
974
* Alias for `createDom`.
975
* @param {string|!goog.dom.TagName<T>} tagName Tag to create.
976
* @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
977
* of name-value pairs for attributes. If a string, then this is the
978
* className of the new element. If an array, the elements will be joined
979
* together as the className of the new element.
980
* @param {...(Object|string|Array|NodeList|null|undefined)} var_args Further
981
* DOM nodes or strings for text nodes. If one of the var_args is an array,
982
* its children will be added as childNodes instead.
983
* @return {R} Reference to a DOM node. The return type is {!Element} if tagName
984
* is a string or a more specific type if it is a member of
985
* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
986
* @template T
987
* @template R := cond(isUnknown(T), 'Element', T) =:
988
* @deprecated Use {@link goog.dom.createDom} instead.
989
*/
990
goog.dom.$dom = goog.dom.createDom;
991
992
993
/**
994
* Creates a new element.
995
* @param {string|!goog.dom.TagName<T>} name Tag to create.
996
* @return {R} The new element. The return type is {!Element} if name is
997
* a string or a more specific type if it is a member of goog.dom.TagName
998
* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
999
* @template T
1000
* @template R := cond(isUnknown(T), 'Element', T) =:
1001
*/
1002
goog.dom.createElement = function(name) {
1003
'use strict';
1004
return goog.dom.createElement_(document, name);
1005
};
1006
1007
1008
/**
1009
* Creates a new element.
1010
* @param {!Document} doc The document to create the element in.
1011
* @param {string|!goog.dom.TagName<T>} name Tag to create.
1012
* @return {R} The new element. The return type is {!Element} if name is
1013
* a string or a more specific type if it is a member of goog.dom.TagName
1014
* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
1015
* @template T
1016
* @template R := cond(isUnknown(T), 'Element', T) =:
1017
* @private
1018
*/
1019
goog.dom.createElement_ = function(doc, name) {
1020
'use strict';
1021
name = String(name);
1022
if (doc.contentType === 'application/xhtml+xml') name = name.toLowerCase();
1023
return doc.createElement(name);
1024
};
1025
1026
1027
/**
1028
* Creates a new text node.
1029
* @param {number|string} content Content.
1030
* @return {!Text} The new text node.
1031
*/
1032
goog.dom.createTextNode = function(content) {
1033
'use strict';
1034
return document.createTextNode(String(content));
1035
};
1036
1037
1038
/**
1039
* Create a table.
1040
* @param {number} rows The number of rows in the table. Must be >= 1.
1041
* @param {number} columns The number of columns in the table. Must be >= 1.
1042
* @param {boolean=} opt_fillWithNbsp If true, fills table entries with
1043
* `goog.string.Unicode.NBSP` characters.
1044
* @return {!Element} The created table.
1045
*/
1046
goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
1047
'use strict';
1048
// TODO(mlourenco): Return HTMLTableElement, also in prototype function.
1049
// Callers need to be updated to e.g. not assign numbers to table.cellSpacing.
1050
return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);
1051
};
1052
1053
1054
/**
1055
* Create a table.
1056
* @param {!Document} doc Document object to use to create the table.
1057
* @param {number} rows The number of rows in the table. Must be >= 1.
1058
* @param {number} columns The number of columns in the table. Must be >= 1.
1059
* @param {boolean} fillWithNbsp If true, fills table entries with
1060
* `goog.string.Unicode.NBSP` characters.
1061
* @return {!HTMLTableElement} The created table.
1062
* @private
1063
*/
1064
goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
1065
'use strict';
1066
var table = goog.dom.createElement_(doc, goog.dom.TagName.TABLE);
1067
var tbody =
1068
table.appendChild(goog.dom.createElement_(doc, goog.dom.TagName.TBODY));
1069
for (var i = 0; i < rows; i++) {
1070
var tr = goog.dom.createElement_(doc, goog.dom.TagName.TR);
1071
for (var j = 0; j < columns; j++) {
1072
var td = goog.dom.createElement_(doc, goog.dom.TagName.TD);
1073
// IE <= 9 will create a text node if we set text content to the empty
1074
// string, so we avoid doing it unless necessary. This ensures that the
1075
// same DOM tree is returned on all browsers.
1076
if (fillWithNbsp) {
1077
goog.dom.setTextContent(td, goog.string.Unicode.NBSP);
1078
}
1079
tr.appendChild(td);
1080
}
1081
tbody.appendChild(tr);
1082
}
1083
return table;
1084
};
1085
1086
1087
1088
/**
1089
* Creates a new Node from constant strings of HTML markup.
1090
* @param {...!goog.string.Const} var_args The HTML strings to concatenate then
1091
* convert into a node.
1092
* @return {!Node}
1093
*/
1094
goog.dom.constHtmlToNode = function(var_args) {
1095
'use strict';
1096
var stringArray =
1097
Array.prototype.map.call(arguments, goog.string.Const.unwrap);
1098
var safeHtml =
1099
goog.html.uncheckedconversions
1100
.safeHtmlFromStringKnownToSatisfyTypeContract(
1101
goog.string.Const.from(
1102
'Constant HTML string, that gets turned into a ' +
1103
'Node later, so it will be automatically balanced.'),
1104
stringArray.join(''));
1105
return goog.dom.safeHtmlToNode(safeHtml);
1106
};
1107
1108
1109
/**
1110
* Converts HTML markup into a node. This is a safe version of
1111
* `goog.dom.htmlToDocumentFragment` which is now deleted.
1112
* @param {!goog.html.SafeHtml} html The HTML markup to convert.
1113
* @return {!Node} The resulting node.
1114
*/
1115
goog.dom.safeHtmlToNode = function(html) {
1116
'use strict';
1117
return goog.dom.safeHtmlToNode_(document, html);
1118
};
1119
1120
1121
/**
1122
* Helper for `safeHtmlToNode`.
1123
* @param {!Document} doc The document.
1124
* @param {!goog.html.SafeHtml} html The HTML markup to convert.
1125
* @return {!Node} The resulting node.
1126
* @private
1127
*/
1128
goog.dom.safeHtmlToNode_ = function(doc, html) {
1129
'use strict';
1130
var tempDiv = goog.dom.createElement_(doc, goog.dom.TagName.DIV);
1131
if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
1132
goog.dom.safe.setInnerHtml(
1133
tempDiv, goog.html.SafeHtml.concat(goog.html.SafeHtml.BR, html));
1134
tempDiv.removeChild(goog.asserts.assert(tempDiv.firstChild));
1135
} else {
1136
goog.dom.safe.setInnerHtml(tempDiv, html);
1137
}
1138
return goog.dom.childrenToNode_(doc, tempDiv);
1139
};
1140
1141
1142
/**
1143
* Helper for `safeHtmlToNode_`.
1144
* @param {!Document} doc The document.
1145
* @param {!Node} tempDiv The input node.
1146
* @return {!Node} The resulting node.
1147
* @private
1148
*/
1149
goog.dom.childrenToNode_ = function(doc, tempDiv) {
1150
'use strict';
1151
if (tempDiv.childNodes.length == 1) {
1152
return tempDiv.removeChild(goog.asserts.assert(tempDiv.firstChild));
1153
} else {
1154
var fragment = doc.createDocumentFragment();
1155
while (tempDiv.firstChild) {
1156
fragment.appendChild(tempDiv.firstChild);
1157
}
1158
return fragment;
1159
}
1160
};
1161
1162
1163
/**
1164
* Returns true if the browser is in "CSS1-compatible" (standards-compliant)
1165
* mode, false otherwise.
1166
* @return {boolean} True if in CSS1-compatible mode.
1167
*/
1168
goog.dom.isCss1CompatMode = function() {
1169
'use strict';
1170
return goog.dom.isCss1CompatMode_(document);
1171
};
1172
1173
1174
/**
1175
* Returns true if the browser is in "CSS1-compatible" (standards-compliant)
1176
* mode, false otherwise.
1177
* @param {!Document} doc The document to check.
1178
* @return {boolean} True if in CSS1-compatible mode.
1179
* @private
1180
*/
1181
goog.dom.isCss1CompatMode_ = function(doc) {
1182
'use strict';
1183
if (goog.dom.COMPAT_MODE_KNOWN_) {
1184
return goog.dom.ASSUME_STANDARDS_MODE;
1185
}
1186
1187
return doc.compatMode == 'CSS1Compat';
1188
};
1189
1190
1191
/**
1192
* Determines if the given node can contain children, intended to be used for
1193
* HTML generation.
1194
*
1195
* IE natively supports node.canHaveChildren but has inconsistent behavior.
1196
* Prior to IE8 the base tag allows children and in IE9 all nodes return true
1197
* for canHaveChildren.
1198
*
1199
* In practice all non-IE browsers allow you to add children to any node, but
1200
* the behavior is inconsistent:
1201
*
1202
* <pre>
1203
* var a = goog.dom.createElement(goog.dom.TagName.BR);
1204
* a.appendChild(document.createTextNode('foo'));
1205
* a.appendChild(document.createTextNode('bar'));
1206
* console.log(a.childNodes.length); // 2
1207
* console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"
1208
* </pre>
1209
*
1210
* For more information, see:
1211
* http://dev.w3.org/html5/markup/syntax.html#syntax-elements
1212
*
1213
* TODO(user): Rename shouldAllowChildren() ?
1214
*
1215
* @param {Node} node The node to check.
1216
* @return {boolean} Whether the node can contain children.
1217
*/
1218
goog.dom.canHaveChildren = function(node) {
1219
'use strict';
1220
if (node.nodeType != goog.dom.NodeType.ELEMENT) {
1221
return false;
1222
}
1223
switch (/** @type {!Element} */ (node).tagName) {
1224
case String(goog.dom.TagName.APPLET):
1225
case String(goog.dom.TagName.AREA):
1226
case String(goog.dom.TagName.BASE):
1227
case String(goog.dom.TagName.BR):
1228
case String(goog.dom.TagName.COL):
1229
case String(goog.dom.TagName.COMMAND):
1230
case String(goog.dom.TagName.EMBED):
1231
case String(goog.dom.TagName.FRAME):
1232
case String(goog.dom.TagName.HR):
1233
case String(goog.dom.TagName.IMG):
1234
case String(goog.dom.TagName.INPUT):
1235
case String(goog.dom.TagName.IFRAME):
1236
case String(goog.dom.TagName.ISINDEX):
1237
case String(goog.dom.TagName.KEYGEN):
1238
case String(goog.dom.TagName.LINK):
1239
case String(goog.dom.TagName.NOFRAMES):
1240
case String(goog.dom.TagName.NOSCRIPT):
1241
case String(goog.dom.TagName.META):
1242
case String(goog.dom.TagName.OBJECT):
1243
case String(goog.dom.TagName.PARAM):
1244
case String(goog.dom.TagName.SCRIPT):
1245
case String(goog.dom.TagName.SOURCE):
1246
case String(goog.dom.TagName.STYLE):
1247
case String(goog.dom.TagName.TRACK):
1248
case String(goog.dom.TagName.WBR):
1249
return false;
1250
}
1251
return true;
1252
};
1253
1254
1255
/**
1256
* Appends a child to a node.
1257
* @param {Node} parent Parent.
1258
* @param {Node} child Child.
1259
*/
1260
goog.dom.appendChild = function(parent, child) {
1261
'use strict';
1262
goog.asserts.assert(
1263
parent != null && child != null,
1264
'goog.dom.appendChild expects non-null arguments');
1265
parent.appendChild(child);
1266
};
1267
1268
1269
/**
1270
* Appends a node with text or other nodes.
1271
* @param {!Node} parent The node to append nodes to.
1272
* @param {...goog.dom.Appendable} var_args The things to append to the node.
1273
* If this is a Node it is appended as is.
1274
* If this is a string then a text node is appended.
1275
* If this is an array like object then fields 0 to length - 1 are appended.
1276
*/
1277
goog.dom.append = function(parent, var_args) {
1278
'use strict';
1279
goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);
1280
};
1281
1282
1283
/**
1284
* Removes all the child nodes on a DOM node.
1285
* @param {Node} node Node to remove children from.
1286
* @return {void}
1287
*/
1288
goog.dom.removeChildren = function(node) {
1289
'use strict';
1290
// Note: Iterations over live collections can be slow, this is the fastest
1291
// we could find. The double parenthesis are used to prevent JsCompiler and
1292
// strict warnings.
1293
var child;
1294
while ((child = node.firstChild)) {
1295
node.removeChild(child);
1296
}
1297
};
1298
1299
1300
/**
1301
* Inserts a new node before an existing reference node (i.e. as the previous
1302
* sibling). If the reference node has no parent, then does nothing.
1303
* @param {Node} newNode Node to insert.
1304
* @param {Node} refNode Reference node to insert before.
1305
*/
1306
goog.dom.insertSiblingBefore = function(newNode, refNode) {
1307
'use strict';
1308
goog.asserts.assert(
1309
newNode != null && refNode != null,
1310
'goog.dom.insertSiblingBefore expects non-null arguments');
1311
if (refNode.parentNode) {
1312
refNode.parentNode.insertBefore(newNode, refNode);
1313
}
1314
};
1315
1316
1317
/**
1318
* Inserts a new node after an existing reference node (i.e. as the next
1319
* sibling). If the reference node has no parent, then does nothing.
1320
* @param {Node} newNode Node to insert.
1321
* @param {Node} refNode Reference node to insert after.
1322
* @return {void}
1323
*/
1324
goog.dom.insertSiblingAfter = function(newNode, refNode) {
1325
'use strict';
1326
goog.asserts.assert(
1327
newNode != null && refNode != null,
1328
'goog.dom.insertSiblingAfter expects non-null arguments');
1329
if (refNode.parentNode) {
1330
refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
1331
}
1332
};
1333
1334
1335
/**
1336
* Insert a child at a given index. If index is larger than the number of child
1337
* nodes that the parent currently has, the node is inserted as the last child
1338
* node.
1339
* @param {Element} parent The element into which to insert the child.
1340
* @param {Node} child The element to insert.
1341
* @param {number} index The index at which to insert the new child node. Must
1342
* not be negative.
1343
* @return {void}
1344
*/
1345
goog.dom.insertChildAt = function(parent, child, index) {
1346
'use strict';
1347
// Note that if the second argument is null, insertBefore
1348
// will append the child at the end of the list of children.
1349
goog.asserts.assert(
1350
parent != null, 'goog.dom.insertChildAt expects a non-null parent');
1351
parent.insertBefore(
1352
/** @type {!Node} */ (child), parent.childNodes[index] || null);
1353
};
1354
1355
1356
/**
1357
* Removes a node from its parent.
1358
* @param {Node} node The node to remove.
1359
* @return {Node} The node removed if removed; else, null.
1360
*/
1361
goog.dom.removeNode = function(node) {
1362
'use strict';
1363
return node && node.parentNode ? node.parentNode.removeChild(node) : null;
1364
};
1365
1366
1367
/**
1368
* Replaces a node in the DOM tree. Will do nothing if `oldNode` has no
1369
* parent.
1370
* @param {Node} newNode Node to insert.
1371
* @param {Node} oldNode Node to replace.
1372
*/
1373
goog.dom.replaceNode = function(newNode, oldNode) {
1374
'use strict';
1375
goog.asserts.assert(
1376
newNode != null && oldNode != null,
1377
'goog.dom.replaceNode expects non-null arguments');
1378
var parent = oldNode.parentNode;
1379
if (parent) {
1380
parent.replaceChild(newNode, oldNode);
1381
}
1382
};
1383
1384
1385
/**
1386
* Replaces child nodes of `target` with child nodes of `source`. This is
1387
* roughly equivalent to `target.innerHTML = source.innerHTML` which is not
1388
* compatible with Trusted Types.
1389
* @param {?Node} target Node to clean and replace its children.
1390
* @param {?Node} source Node to get the children from. The nodes will be cloned
1391
* so they will stay in source.
1392
*/
1393
goog.dom.copyContents = function(target, source) {
1394
'use strict';
1395
goog.asserts.assert(
1396
target != null && source != null,
1397
'goog.dom.copyContents expects non-null arguments');
1398
var childNodes = source.cloneNode(/* deep= */ true).childNodes;
1399
goog.dom.removeChildren(target);
1400
while (childNodes.length) {
1401
target.appendChild(childNodes[0]);
1402
}
1403
};
1404
1405
1406
/**
1407
* Flattens an element. That is, removes it and replace it with its children.
1408
* Does nothing if the element is not in the document.
1409
* @param {Element} element The element to flatten.
1410
* @return {Element|undefined} The original element, detached from the document
1411
* tree, sans children; or undefined, if the element was not in the document
1412
* to begin with.
1413
*/
1414
goog.dom.flattenElement = function(element) {
1415
'use strict';
1416
var child, parent = element.parentNode;
1417
if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
1418
// Use IE DOM method (supported by Opera too) if available
1419
if (element.removeNode) {
1420
return /** @type {Element} */ (element.removeNode(false));
1421
} else {
1422
// Move all children of the original node up one level.
1423
while ((child = element.firstChild)) {
1424
parent.insertBefore(child, element);
1425
}
1426
1427
// Detach the original element.
1428
return /** @type {Element} */ (goog.dom.removeNode(element));
1429
}
1430
}
1431
};
1432
1433
1434
/**
1435
* Returns an array containing just the element children of the given element.
1436
* @param {Element} element The element whose element children we want.
1437
* @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list
1438
* of just the element children of the given element.
1439
*/
1440
goog.dom.getChildren = function(element) {
1441
'use strict';
1442
// We check if the children attribute is supported for child elements
1443
// since IE8 misuses the attribute by also including comments.
1444
if (element.children != undefined) {
1445
return element.children;
1446
}
1447
// Fall back to manually filtering the element's child nodes.
1448
return Array.prototype.filter.call(element.childNodes, function(node) {
1449
return node.nodeType == goog.dom.NodeType.ELEMENT;
1450
});
1451
};
1452
1453
1454
/**
1455
* Returns the first child node that is an element.
1456
* @param {Node} node The node to get the first child element of.
1457
* @return {Element} The first child node of `node` that is an element.
1458
*/
1459
goog.dom.getFirstElementChild = function(node) {
1460
'use strict';
1461
if (node.firstElementChild !== undefined) {
1462
return /** @type {!Element} */ (node).firstElementChild;
1463
}
1464
return goog.dom.getNextElementNode_(node.firstChild, true);
1465
};
1466
1467
1468
/**
1469
* Returns the last child node that is an element.
1470
* @param {Node} node The node to get the last child element of.
1471
* @return {Element} The last child node of `node` that is an element.
1472
*/
1473
goog.dom.getLastElementChild = function(node) {
1474
'use strict';
1475
if (node.lastElementChild !== undefined) {
1476
return /** @type {!Element} */ (node).lastElementChild;
1477
}
1478
return goog.dom.getNextElementNode_(node.lastChild, false);
1479
};
1480
1481
1482
/**
1483
* Returns the first next sibling that is an element.
1484
* @param {Node} node The node to get the next sibling element of.
1485
* @return {Element} The next sibling of `node` that is an element.
1486
*/
1487
goog.dom.getNextElementSibling = function(node) {
1488
'use strict';
1489
if (node.nextElementSibling !== undefined) {
1490
return /** @type {!Element} */ (node).nextElementSibling;
1491
}
1492
return goog.dom.getNextElementNode_(node.nextSibling, true);
1493
};
1494
1495
1496
/**
1497
* Returns the first previous sibling that is an element.
1498
* @param {Node} node The node to get the previous sibling element of.
1499
* @return {Element} The first previous sibling of `node` that is
1500
* an element.
1501
*/
1502
goog.dom.getPreviousElementSibling = function(node) {
1503
'use strict';
1504
if (node.previousElementSibling !== undefined) {
1505
return /** @type {!Element} */ (node).previousElementSibling;
1506
}
1507
return goog.dom.getNextElementNode_(node.previousSibling, false);
1508
};
1509
1510
1511
/**
1512
* Returns the first node that is an element in the specified direction,
1513
* starting with `node`.
1514
* @param {Node} node The node to get the next element from.
1515
* @param {boolean} forward Whether to look forwards or backwards.
1516
* @return {Element} The first element.
1517
* @private
1518
*/
1519
goog.dom.getNextElementNode_ = function(node, forward) {
1520
'use strict';
1521
while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {
1522
node = forward ? node.nextSibling : node.previousSibling;
1523
}
1524
1525
return /** @type {Element} */ (node);
1526
};
1527
1528
1529
/**
1530
* Returns the next node in source order from the given node.
1531
* @param {Node} node The node.
1532
* @return {Node} The next node in the DOM tree, or null if this was the last
1533
* node.
1534
*/
1535
goog.dom.getNextNode = function(node) {
1536
'use strict';
1537
if (!node) {
1538
return null;
1539
}
1540
1541
if (node.firstChild) {
1542
return node.firstChild;
1543
}
1544
1545
while (node && !node.nextSibling) {
1546
node = node.parentNode;
1547
}
1548
1549
return node ? node.nextSibling : null;
1550
};
1551
1552
1553
/**
1554
* Returns the previous node in source order from the given node.
1555
* @param {Node} node The node.
1556
* @return {Node} The previous node in the DOM tree, or null if this was the
1557
* first node.
1558
*/
1559
goog.dom.getPreviousNode = function(node) {
1560
'use strict';
1561
if (!node) {
1562
return null;
1563
}
1564
1565
if (!node.previousSibling) {
1566
return node.parentNode;
1567
}
1568
1569
node = node.previousSibling;
1570
while (node && node.lastChild) {
1571
node = node.lastChild;
1572
}
1573
1574
return node;
1575
};
1576
1577
1578
/**
1579
* Whether the object looks like a DOM node.
1580
* @param {?} obj The object being tested for node likeness.
1581
* @return {boolean} Whether the object looks like a DOM node.
1582
*/
1583
goog.dom.isNodeLike = function(obj) {
1584
'use strict';
1585
return goog.utils.isObject(obj) && obj.nodeType > 0;
1586
};
1587
1588
1589
/**
1590
* Whether the object looks like an Element.
1591
* @param {?} obj The object being tested for Element likeness.
1592
* @return {boolean} Whether the object looks like an Element.
1593
*/
1594
goog.dom.isElement = function(obj) {
1595
'use strict';
1596
return goog.utils.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;
1597
};
1598
1599
1600
/**
1601
* Returns true if the specified value is a Window object. This includes the
1602
* global window for HTML pages, and iframe windows.
1603
* @param {?} obj Variable to test.
1604
* @return {boolean} Whether the variable is a window.
1605
*/
1606
goog.dom.isWindow = function(obj) {
1607
'use strict';
1608
return goog.utils.isObject(obj) && obj['window'] == obj;
1609
};
1610
1611
1612
/**
1613
* Returns an element's parent, if it's an Element.
1614
* @param {Element} element The DOM element.
1615
* @return {Element} The parent, or null if not an Element.
1616
*/
1617
goog.dom.getParentElement = function(element) {
1618
'use strict';
1619
var parent;
1620
if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {
1621
parent = element.parentElement;
1622
if (parent) {
1623
return parent;
1624
}
1625
}
1626
parent = element.parentNode;
1627
return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;
1628
};
1629
1630
1631
/**
1632
* Whether a node contains another node.
1633
* @param {?Node|undefined} parent The node that should contain the other node.
1634
* @param {?Node|undefined} descendant The node to test presence of.
1635
* @return {boolean} Whether the parent node contains the descendant node.
1636
*/
1637
goog.dom.contains = function(parent, descendant) {
1638
'use strict';
1639
if (!parent || !descendant) {
1640
return false;
1641
}
1642
// We use browser specific methods for this if available since it is faster
1643
// that way.
1644
1645
// IE DOM
1646
if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
1647
return parent == descendant || parent.contains(descendant);
1648
}
1649
1650
// W3C DOM Level 3
1651
if (typeof parent.compareDocumentPosition != 'undefined') {
1652
return parent == descendant ||
1653
Boolean(parent.compareDocumentPosition(descendant) & 16);
1654
}
1655
1656
// W3C DOM Level 1
1657
while (descendant && parent != descendant) {
1658
descendant = descendant.parentNode;
1659
}
1660
return descendant == parent;
1661
};
1662
1663
1664
/**
1665
* Compares the document order of two nodes, returning 0 if they are the same
1666
* node, a negative number if node1 is before node2, and a positive number if
1667
* node2 is before node1. Note that we compare the order the tags appear in the
1668
* document so in the tree <b><i>text</i></b> the B node is considered to be
1669
* before the I node.
1670
*
1671
* @param {Node} node1 The first node to compare.
1672
* @param {Node} node2 The second node to compare.
1673
* @return {number} 0 if the nodes are the same node, a negative number if node1
1674
* is before node2, and a positive number if node2 is before node1.
1675
*/
1676
goog.dom.compareNodeOrder = function(node1, node2) {
1677
'use strict';
1678
// Fall out quickly for equality.
1679
if (node1 == node2) {
1680
return 0;
1681
}
1682
1683
// Use compareDocumentPosition where available
1684
if (node1.compareDocumentPosition) {
1685
// 4 is the bitmask for FOLLOWS.
1686
return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;
1687
}
1688
1689
// Special case for document nodes on IE 7 and 8.
1690
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
1691
if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
1692
return -1;
1693
}
1694
if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {
1695
return 1;
1696
}
1697
}
1698
1699
// Process in IE using sourceIndex - we check to see if the first node has
1700
// a source index or if its parent has one.
1701
if ('sourceIndex' in node1 ||
1702
(node1.parentNode && 'sourceIndex' in node1.parentNode)) {
1703
var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;
1704
var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;
1705
1706
if (isElement1 && isElement2) {
1707
return node1.sourceIndex - node2.sourceIndex;
1708
} else {
1709
var parent1 = node1.parentNode;
1710
var parent2 = node2.parentNode;
1711
1712
if (parent1 == parent2) {
1713
return goog.dom.compareSiblingOrder_(node1, node2);
1714
}
1715
1716
if (!isElement1 && goog.dom.contains(parent1, node2)) {
1717
return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);
1718
}
1719
1720
1721
if (!isElement2 && goog.dom.contains(parent2, node1)) {
1722
return goog.dom.compareParentsDescendantNodeIe_(node2, node1);
1723
}
1724
1725
return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -
1726
(isElement2 ? node2.sourceIndex : parent2.sourceIndex);
1727
}
1728
}
1729
1730
// For Safari, we compare ranges.
1731
var doc = goog.dom.getOwnerDocument(node1);
1732
1733
var range1, range2;
1734
range1 = doc.createRange();
1735
range1.selectNode(node1);
1736
range1.collapse(true);
1737
1738
range2 = doc.createRange();
1739
range2.selectNode(node2);
1740
range2.collapse(true);
1741
1742
return range1.compareBoundaryPoints(
1743
goog.global['Range'].START_TO_END, range2);
1744
};
1745
1746
1747
/**
1748
* Utility function to compare the position of two nodes, when
1749
* `textNode`'s parent is an ancestor of `node`. If this entry
1750
* condition is not met, this function will attempt to reference a null object.
1751
* @param {!Node} textNode The textNode to compare.
1752
* @param {Node} node The node to compare.
1753
* @return {number} -1 if node is before textNode, +1 otherwise.
1754
* @private
1755
*/
1756
goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {
1757
'use strict';
1758
var parent = textNode.parentNode;
1759
if (parent == node) {
1760
// If textNode is a child of node, then node comes first.
1761
return -1;
1762
}
1763
var sibling = node;
1764
while (sibling.parentNode != parent) {
1765
sibling = sibling.parentNode;
1766
}
1767
return goog.dom.compareSiblingOrder_(sibling, textNode);
1768
};
1769
1770
1771
/**
1772
* Utility function to compare the position of two nodes known to be non-equal
1773
* siblings.
1774
* @param {Node} node1 The first node to compare.
1775
* @param {!Node} node2 The second node to compare.
1776
* @return {number} -1 if node1 is before node2, +1 otherwise.
1777
* @private
1778
*/
1779
goog.dom.compareSiblingOrder_ = function(node1, node2) {
1780
'use strict';
1781
var s = node2;
1782
while ((s = s.previousSibling)) {
1783
if (s == node1) {
1784
// We just found node1 before node2.
1785
return -1;
1786
}
1787
}
1788
1789
// Since we didn't find it, node1 must be after node2.
1790
return 1;
1791
};
1792
1793
1794
/**
1795
* Find the deepest common ancestor of the given nodes.
1796
* @param {...Node} var_args The nodes to find a common ancestor of.
1797
* @return {Node} The common ancestor of the nodes, or null if there is none.
1798
* null will only be returned if two or more of the nodes are from different
1799
* documents.
1800
*/
1801
goog.dom.findCommonAncestor = function(var_args) {
1802
'use strict';
1803
var i, count = arguments.length;
1804
if (!count) {
1805
return null;
1806
} else if (count == 1) {
1807
return arguments[0];
1808
}
1809
1810
var paths = [];
1811
var minLength = Infinity;
1812
for (i = 0; i < count; i++) {
1813
// Compute the list of ancestors.
1814
var ancestors = [];
1815
var node = arguments[i];
1816
while (node) {
1817
ancestors.unshift(node);
1818
node = node.parentNode;
1819
}
1820
1821
// Save the list for comparison.
1822
paths.push(ancestors);
1823
minLength = Math.min(minLength, ancestors.length);
1824
}
1825
var output = null;
1826
for (i = 0; i < minLength; i++) {
1827
var first = paths[0][i];
1828
for (var j = 1; j < count; j++) {
1829
if (first != paths[j][i]) {
1830
return output;
1831
}
1832
}
1833
output = first;
1834
}
1835
return output;
1836
};
1837
1838
1839
/**
1840
* Returns whether node is in a document or detached. Throws an error if node
1841
* itself is a document. This specifically handles two cases beyond naive use of
1842
* builtins: (1) it works correctly in IE, and (2) it works for elements from
1843
* different documents/iframes. If neither of these considerations are relevant
1844
* then a simple `document.contains(node)` may be used instead.
1845
* @param {!Node} node
1846
* @return {boolean}
1847
*/
1848
goog.dom.isInDocument = function(node) {
1849
'use strict';
1850
return (node.ownerDocument.compareDocumentPosition(node) & 16) == 16;
1851
};
1852
1853
1854
/**
1855
* Returns the owner document for a node.
1856
* @param {Node|Window} node The node to get the document for.
1857
* @return {!Document} The document owning the node.
1858
*/
1859
goog.dom.getOwnerDocument = function(node) {
1860
'use strict';
1861
// TODO(nnaze): Update param signature to be non-nullable.
1862
goog.asserts.assert(node, 'Node cannot be null or undefined.');
1863
return /** @type {!Document} */ (
1864
node.nodeType == goog.dom.NodeType.DOCUMENT ?
1865
node :
1866
node.ownerDocument || node.document);
1867
};
1868
1869
1870
/**
1871
* Cross-browser function for getting the document element of a frame or iframe.
1872
* @param {Element} frame Frame element.
1873
* @return {!Document} The frame content document.
1874
*/
1875
goog.dom.getFrameContentDocument = function(frame) {
1876
'use strict';
1877
return frame.contentDocument ||
1878
/** @type {!HTMLFrameElement} */ (frame).contentWindow.document;
1879
};
1880
1881
1882
/**
1883
* Cross-browser function for getting the window of a frame or iframe.
1884
* @param {Element} frame Frame element.
1885
* @return {Window} The window associated with the given frame, or null if none
1886
* exists.
1887
*/
1888
goog.dom.getFrameContentWindow = function(frame) {
1889
'use strict';
1890
try {
1891
return frame.contentWindow ||
1892
(frame.contentDocument ? goog.dom.getWindow(frame.contentDocument) :
1893
null);
1894
} catch (e) {
1895
// NOTE(user): In IE8, checking the contentWindow or contentDocument
1896
// properties will throw a "Unspecified Error" exception if the iframe is
1897
// not inserted in the DOM. If we get this we can be sure that no window
1898
// exists, so return null.
1899
}
1900
return null;
1901
};
1902
1903
1904
/**
1905
* Sets the text content of a node, with cross-browser support.
1906
* @param {Node} node The node to change the text content of.
1907
* @param {string|number} text The value that should replace the node's content.
1908
* @return {void}
1909
*/
1910
goog.dom.setTextContent = function(node, text) {
1911
'use strict';
1912
goog.asserts.assert(
1913
node != null,
1914
'goog.dom.setTextContent expects a non-null value for node');
1915
1916
if ('textContent' in node) {
1917
node.textContent = text;
1918
} else if (node.nodeType == goog.dom.NodeType.TEXT) {
1919
/** @type {!Text} */ (node).data = String(text);
1920
} else if (
1921
node.firstChild && node.firstChild.nodeType == goog.dom.NodeType.TEXT) {
1922
// If the first child is a text node we just change its data and remove the
1923
// rest of the children.
1924
while (node.lastChild != node.firstChild) {
1925
node.removeChild(goog.asserts.assert(node.lastChild));
1926
}
1927
/** @type {!Text} */ (node.firstChild).data = String(text);
1928
} else {
1929
goog.dom.removeChildren(node);
1930
var doc = goog.dom.getOwnerDocument(node);
1931
node.appendChild(doc.createTextNode(String(text)));
1932
}
1933
};
1934
1935
1936
/**
1937
* Gets the outerHTML of a node, which is like innerHTML, except that it
1938
* actually contains the HTML of the node itself.
1939
* @param {Element} element The element to get the HTML of.
1940
* @return {string} The outerHTML of the given element.
1941
*/
1942
goog.dom.getOuterHtml = function(element) {
1943
'use strict';
1944
goog.asserts.assert(
1945
element !== null,
1946
'goog.dom.getOuterHtml expects a non-null value for element');
1947
// IE, Opera and WebKit all have outerHTML.
1948
if ('outerHTML' in element) {
1949
return element.outerHTML;
1950
} else {
1951
var doc = goog.dom.getOwnerDocument(element);
1952
var div = goog.dom.createElement_(doc, goog.dom.TagName.DIV);
1953
div.appendChild(element.cloneNode(true));
1954
return div.innerHTML;
1955
}
1956
};
1957
1958
1959
/**
1960
* Finds the first descendant node that matches the filter function, using depth
1961
* first search. This function offers the most general purpose way of finding a
1962
* matching element.
1963
*
1964
* Prefer using `querySelector` if the matching criteria can be expressed as a
1965
* CSS selector, or `goog.dom.findElement` if you would filter for `nodeType ==
1966
* Node.ELEMENT_NODE`.
1967
*
1968
* @param {Node} root The root of the tree to search.
1969
* @param {function(Node) : boolean} p The filter function.
1970
* @return {Node|undefined} The found node or undefined if none is found.
1971
*/
1972
goog.dom.findNode = function(root, p) {
1973
'use strict';
1974
var rv = [];
1975
var found = goog.dom.findNodes_(root, p, rv, true);
1976
return found ? rv[0] : undefined;
1977
};
1978
1979
1980
/**
1981
* Finds all the descendant nodes that match the filter function, using depth
1982
* first search. This function offers the most general-purpose way
1983
* of finding a set of matching elements.
1984
*
1985
* Prefer using `querySelectorAll` if the matching criteria can be expressed as
1986
* a CSS selector, or `goog.dom.findElements` if you would filter for
1987
* `nodeType == Node.ELEMENT_NODE`.
1988
*
1989
* @param {Node} root The root of the tree to search.
1990
* @param {function(Node) : boolean} p The filter function.
1991
* @return {!Array<!Node>} The found nodes or an empty array if none are found.
1992
*/
1993
goog.dom.findNodes = function(root, p) {
1994
'use strict';
1995
var rv = [];
1996
goog.dom.findNodes_(root, p, rv, false);
1997
return rv;
1998
};
1999
2000
2001
/**
2002
* Finds the first or all the descendant nodes that match the filter function,
2003
* using a depth first search.
2004
* @param {Node} root The root of the tree to search.
2005
* @param {function(Node) : boolean} p The filter function.
2006
* @param {!Array<!Node>} rv The found nodes are added to this array.
2007
* @param {boolean} findOne If true we exit after the first found node.
2008
* @return {boolean} Whether the search is complete or not. True in case findOne
2009
* is true and the node is found. False otherwise.
2010
* @private
2011
*/
2012
goog.dom.findNodes_ = function(root, p, rv, findOne) {
2013
'use strict';
2014
if (root != null) {
2015
var child = root.firstChild;
2016
while (child) {
2017
if (p(child)) {
2018
rv.push(child);
2019
if (findOne) {
2020
return true;
2021
}
2022
}
2023
if (goog.dom.findNodes_(child, p, rv, findOne)) {
2024
return true;
2025
}
2026
child = child.nextSibling;
2027
}
2028
}
2029
return false;
2030
};
2031
2032
2033
/**
2034
* Finds the first descendant element (excluding `root`) that matches the filter
2035
* function, using depth first search. Prefer using `querySelector` if the
2036
* matching criteria can be expressed as a CSS selector.
2037
*
2038
* @param {!Element | !Document} root
2039
* @param {function(!Element): boolean} pred Filter function.
2040
* @return {?Element} First matching element or null if there is none.
2041
*/
2042
goog.dom.findElement = function(root, pred) {
2043
'use strict';
2044
var stack = goog.dom.getChildrenReverse_(root);
2045
while (stack.length > 0) {
2046
var next = stack.pop();
2047
if (pred(next)) return next;
2048
for (var c = next.lastElementChild; c; c = c.previousElementSibling) {
2049
stack.push(c);
2050
}
2051
}
2052
return null;
2053
};
2054
2055
2056
/**
2057
* Finds all the descendant elements (excluding `root`) that match the filter
2058
* function, using depth first search. Prefer using `querySelectorAll` if the
2059
* matching criteria can be expressed as a CSS selector.
2060
*
2061
* @param {!Element | !Document} root
2062
* @param {function(!Element): boolean} pred Filter function.
2063
* @return {!Array<!Element>}
2064
*/
2065
goog.dom.findElements = function(root, pred) {
2066
'use strict';
2067
var result = [], stack = goog.dom.getChildrenReverse_(root);
2068
while (stack.length > 0) {
2069
var next = stack.pop();
2070
if (pred(next)) result.push(next);
2071
for (var c = next.lastElementChild; c; c = c.previousElementSibling) {
2072
stack.push(c);
2073
}
2074
}
2075
return result;
2076
};
2077
2078
2079
/**
2080
* @param {!Element | !Document} node
2081
* @return {!Array<!Element>} node's child elements in reverse order.
2082
* @private
2083
*/
2084
goog.dom.getChildrenReverse_ = function(node) {
2085
'use strict';
2086
// document.lastElementChild doesn't exist in IE9; fall back to
2087
// documentElement.
2088
if (node.nodeType == goog.dom.NodeType.DOCUMENT) {
2089
return [node.documentElement];
2090
} else {
2091
var children = [];
2092
for (var c = node.lastElementChild; c; c = c.previousElementSibling) {
2093
children.push(c);
2094
}
2095
return children;
2096
}
2097
};
2098
2099
2100
/**
2101
* Map of tags whose content to ignore when calculating text length.
2102
* @private {!Object<string, number>}
2103
* @const
2104
*/
2105
goog.dom.TAGS_TO_IGNORE_ = {
2106
'SCRIPT': 1,
2107
'STYLE': 1,
2108
'HEAD': 1,
2109
'IFRAME': 1,
2110
'OBJECT': 1
2111
};
2112
2113
2114
/**
2115
* Map of tags which have predefined values with regard to whitespace.
2116
* @private {!Object<string, string>}
2117
* @const
2118
*/
2119
goog.dom.PREDEFINED_TAG_VALUES_ = {
2120
'IMG': ' ',
2121
'BR': '\n'
2122
};
2123
2124
2125
/**
2126
* Returns true if the element has a tab index that allows it to receive
2127
* keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
2128
* natively support keyboard focus, even if they have no tab index.
2129
* @param {!Element} element Element to check.
2130
* @return {boolean} Whether the element has a tab index that allows keyboard
2131
* focus.
2132
*/
2133
goog.dom.isFocusableTabIndex = function(element) {
2134
'use strict';
2135
return goog.dom.hasSpecifiedTabIndex_(element) &&
2136
goog.dom.isTabIndexFocusable_(element);
2137
};
2138
2139
2140
/**
2141
* Enables or disables keyboard focus support on the element via its tab index.
2142
* Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
2143
* (or elements that natively support keyboard focus, like form elements) can
2144
* receive keyboard focus. See http://go/tabindex for more info.
2145
* @param {Element} element Element whose tab index is to be changed.
2146
* @param {boolean} enable Whether to set or remove a tab index on the element
2147
* that supports keyboard focus.
2148
* @return {void}
2149
*/
2150
goog.dom.setFocusableTabIndex = function(element, enable) {
2151
'use strict';
2152
if (enable) {
2153
element.tabIndex = 0;
2154
} else {
2155
// Set tabIndex to -1 first, then remove it. This is a workaround for
2156
// Safari (confirmed in version 4 on Windows). When removing the attribute
2157
// without setting it to -1 first, the element remains keyboard focusable
2158
// despite not having a tabIndex attribute anymore.
2159
element.tabIndex = -1;
2160
element.removeAttribute('tabIndex'); // Must be camelCase!
2161
}
2162
};
2163
2164
2165
/**
2166
* Returns true if the element can be focused, i.e. it has a tab index that
2167
* allows it to receive keyboard focus (tabIndex >= 0), or it is an element
2168
* that natively supports keyboard focus.
2169
* @param {!Element} element Element to check.
2170
* @return {boolean} Whether the element allows keyboard focus.
2171
*/
2172
goog.dom.isFocusable = function(element) {
2173
'use strict';
2174
var focusable;
2175
// Some elements can have unspecified tab index and still receive focus.
2176
if (goog.dom.nativelySupportsFocus_(element)) {
2177
// Make sure the element is not disabled ...
2178
focusable = !element.disabled &&
2179
// ... and if a tab index is specified, it allows focus.
2180
(!goog.dom.hasSpecifiedTabIndex_(element) ||
2181
goog.dom.isTabIndexFocusable_(element));
2182
} else {
2183
focusable = goog.dom.isFocusableTabIndex(element);
2184
}
2185
2186
// IE requires elements to be visible in order to focus them.
2187
return focusable && goog.userAgent.IE ?
2188
goog.dom.hasNonZeroBoundingRect_(/** @type {!HTMLElement} */ (element)) :
2189
focusable;
2190
};
2191
2192
2193
/**
2194
* Returns true if the element has a specified tab index.
2195
* @param {!Element} element Element to check.
2196
* @return {boolean} Whether the element has a specified tab index.
2197
* @private
2198
*/
2199
goog.dom.hasSpecifiedTabIndex_ = function(element) {
2200
'use strict';
2201
return element.hasAttribute('tabindex');
2202
};
2203
2204
2205
/**
2206
* Returns true if the element's tab index allows the element to be focused.
2207
* @param {!Element} element Element to check.
2208
* @return {boolean} Whether the element's tab index allows focus.
2209
* @private
2210
*/
2211
goog.dom.isTabIndexFocusable_ = function(element) {
2212
'use strict';
2213
var index = /** @type {!HTMLElement} */ (element).tabIndex;
2214
// NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.
2215
return typeof (index) === 'number' && index >= 0 && index < 32768;
2216
};
2217
2218
2219
/**
2220
* Returns true if the element is focusable even when tabIndex is not set.
2221
* @param {!Element} element Element to check.
2222
* @return {boolean} Whether the element natively supports focus.
2223
* @private
2224
*/
2225
goog.dom.nativelySupportsFocus_ = function(element) {
2226
'use strict';
2227
return (
2228
element.tagName == goog.dom.TagName.A && element.hasAttribute('href') ||
2229
element.tagName == goog.dom.TagName.INPUT ||
2230
element.tagName == goog.dom.TagName.TEXTAREA ||
2231
element.tagName == goog.dom.TagName.SELECT ||
2232
element.tagName == goog.dom.TagName.BUTTON);
2233
};
2234
2235
2236
/**
2237
* Returns true if the element has a bounding rectangle that would be visible
2238
* (i.e. its width and height are greater than zero).
2239
* @param {!HTMLElement} element Element to check.
2240
* @return {boolean} Whether the element has a non-zero bounding rectangle.
2241
* @private
2242
*/
2243
goog.dom.hasNonZeroBoundingRect_ = function(element) {
2244
'use strict';
2245
var rect;
2246
if (typeof element['getBoundingClientRect'] !== 'function' ||
2247
// In IE, getBoundingClientRect throws on detached nodes.
2248
(goog.userAgent.IE && element.parentElement == null)) {
2249
rect = {'height': element.offsetHeight, 'width': element.offsetWidth};
2250
} else {
2251
rect = element.getBoundingClientRect();
2252
}
2253
return rect != null && rect.height > 0 && rect.width > 0;
2254
};
2255
2256
2257
/**
2258
* Returns the text content of the current node, without markup and invisible
2259
* symbols. New lines are stripped and whitespace is collapsed,
2260
* such that each character would be visible.
2261
*
2262
* In browsers that support it, innerText is used. Other browsers attempt to
2263
* simulate it via node traversal. Line breaks are canonicalized in IE.
2264
*
2265
* @param {Node} node The node from which we are getting content.
2266
* @return {string} The text content.
2267
*/
2268
goog.dom.getTextContent = function(node) {
2269
'use strict';
2270
var textContent;
2271
var buf = [];
2272
goog.dom.getTextContent_(node, buf, true);
2273
textContent = buf.join('');
2274
2275
// Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.
2276
textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');
2277
// Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.
2278
textContent = textContent.replace(/\u200B/g, '');
2279
2280
textContent = textContent.replace(/ +/g, ' ');
2281
if (textContent != ' ') {
2282
textContent = textContent.replace(/^\s*/, '');
2283
}
2284
2285
return textContent;
2286
};
2287
2288
2289
/**
2290
* Returns the text content of the current node, without markup.
2291
*
2292
* Unlike `getTextContent` this method does not collapse whitespaces
2293
* or normalize lines breaks.
2294
*
2295
* @param {Node} node The node from which we are getting content.
2296
* @return {string} The raw text content.
2297
*/
2298
goog.dom.getRawTextContent = function(node) {
2299
'use strict';
2300
var buf = [];
2301
goog.dom.getTextContent_(node, buf, false);
2302
2303
return buf.join('');
2304
};
2305
2306
2307
/**
2308
* Recursive support function for text content retrieval.
2309
*
2310
* @param {Node} node The node from which we are getting content.
2311
* @param {Array<string>} buf string buffer.
2312
* @param {boolean} normalizeWhitespace Whether to normalize whitespace.
2313
* @private
2314
*/
2315
goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
2316
'use strict';
2317
if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
2318
// ignore certain tags
2319
} else if (node.nodeType == goog.dom.NodeType.TEXT) {
2320
if (normalizeWhitespace) {
2321
buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
2322
} else {
2323
buf.push(node.nodeValue);
2324
}
2325
} else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
2326
buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);
2327
} else {
2328
var child = node.firstChild;
2329
while (child) {
2330
goog.dom.getTextContent_(child, buf, normalizeWhitespace);
2331
child = child.nextSibling;
2332
}
2333
}
2334
};
2335
2336
2337
/**
2338
* Returns the text length of the text contained in a node, without markup. This
2339
* is equivalent to the selection length if the node was selected, or the number
2340
* of cursor movements to traverse the node. Images & BRs take one space. New
2341
* lines are ignored.
2342
*
2343
* @param {Node} node The node whose text content length is being calculated.
2344
* @return {number} The length of `node`'s text content.
2345
*/
2346
goog.dom.getNodeTextLength = function(node) {
2347
'use strict';
2348
return goog.dom.getTextContent(node).length;
2349
};
2350
2351
2352
/**
2353
* Returns the text offset of a node relative to one of its ancestors. The text
2354
* length is the same as the length calculated by goog.dom.getNodeTextLength.
2355
*
2356
* @param {Node} node The node whose offset is being calculated.
2357
* @param {Node=} opt_offsetParent The node relative to which the offset will
2358
* be calculated. Defaults to the node's owner document's body.
2359
* @return {number} The text offset.
2360
*/
2361
goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {
2362
'use strict';
2363
var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;
2364
var buf = [];
2365
while (node && node != root) {
2366
var cur = node;
2367
while ((cur = cur.previousSibling)) {
2368
buf.unshift(goog.dom.getTextContent(cur));
2369
}
2370
node = node.parentNode;
2371
}
2372
// Trim left to deal with FF cases when there might be line breaks and empty
2373
// nodes at the front of the text
2374
return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;
2375
};
2376
2377
2378
/**
2379
* Returns the node at a given offset in a parent node. If an object is
2380
* provided for the optional third parameter, the node and the remainder of the
2381
* offset will stored as properties of this object.
2382
* @param {Node} parent The parent node.
2383
* @param {number} offset The offset into the parent node.
2384
* @param {Object=} opt_result Object to be used to store the return value. The
2385
* return value will be stored in the form {node: Node, remainder: number}
2386
* if this object is provided.
2387
* @return {Node} The node at the given offset.
2388
*/
2389
goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
2390
'use strict';
2391
var stack = [parent], pos = 0, cur = null;
2392
while (stack.length > 0 && pos < offset) {
2393
cur = stack.pop();
2394
if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
2395
// ignore certain tags
2396
} else if (cur.nodeType == goog.dom.NodeType.TEXT) {
2397
var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' ');
2398
pos += text.length;
2399
} else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
2400
pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;
2401
} else {
2402
for (var i = cur.childNodes.length - 1; i >= 0; i--) {
2403
stack.push(cur.childNodes[i]);
2404
}
2405
}
2406
}
2407
if (goog.utils.isObject(opt_result)) {
2408
opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;
2409
opt_result.node = cur;
2410
}
2411
2412
return cur;
2413
};
2414
2415
2416
/**
2417
* Returns true if the object is a `NodeList`. To qualify as a NodeList,
2418
* the object must have a numeric length property and an item function (which
2419
* has type 'string' on IE for some reason).
2420
* @param {Object} val Object to test.
2421
* @return {boolean} Whether the object is a NodeList.
2422
*/
2423
goog.dom.isNodeList = function(val) {
2424
'use strict';
2425
// TODO(attila): Now the isNodeList is part of goog.dom we can use
2426
// goog.userAgent to make this simpler.
2427
// A NodeList must have a length property of type 'number' on all platforms.
2428
if (val && typeof val.length == 'number') {
2429
// A NodeList is an object everywhere except Safari, where it's a function.
2430
if (goog.utils.isObject(val)) {
2431
// A NodeList must have an item function (on non-IE platforms) or an item
2432
// property of type 'string' (on IE).
2433
return typeof val.item == 'function' || typeof val.item == 'string';
2434
} else if (typeof val === 'function') {
2435
// On Safari, a NodeList is a function with an item property that is also
2436
// a function.
2437
return typeof /** @type {?} */ (val.item) == 'function';
2438
}
2439
}
2440
2441
// Not a NodeList.
2442
return false;
2443
};
2444
2445
2446
/**
2447
* Walks up the DOM hierarchy returning the first ancestor that has the passed
2448
* tag name and/or class name. If the passed element matches the specified
2449
* criteria, the element itself is returned.
2450
* @param {Node} element The DOM node to start with.
2451
* @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or
2452
* null/undefined to match only based on class name).
2453
* @param {?string=} opt_class The class name to match (or null/undefined to
2454
* match only based on tag name).
2455
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
2456
* dom.
2457
* @return {?R} The first ancestor that matches the passed criteria, or
2458
* null if no match is found. The return type is {?Element} if opt_tag is
2459
* not a member of goog.dom.TagName or a more specific type if it is (e.g.
2460
* {?HTMLAnchorElement} for goog.dom.TagName.A).
2461
* @template T
2462
* @template R := cond(isUnknown(T), 'Element', T) =:
2463
*/
2464
goog.dom.getAncestorByTagNameAndClass = function(
2465
element, opt_tag, opt_class, opt_maxSearchSteps) {
2466
'use strict';
2467
if (!opt_tag && !opt_class) {
2468
return null;
2469
}
2470
var tagName = opt_tag ? String(opt_tag).toUpperCase() : null;
2471
return /** @type {Element} */ (goog.dom.getAncestor(element, function(node) {
2472
'use strict';
2473
return (!tagName || node.nodeName == tagName) &&
2474
(!opt_class ||
2475
typeof node.className === 'string' &&
2476
goog.array.contains(node.className.split(/\s+/), opt_class));
2477
}, true, opt_maxSearchSteps));
2478
};
2479
2480
2481
/**
2482
* Walks up the DOM hierarchy returning the first ancestor that has the passed
2483
* class name. If the passed element matches the specified criteria, the
2484
* element itself is returned.
2485
* @param {Node} element The DOM node to start with.
2486
* @param {string} className The class name to match.
2487
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
2488
* dom.
2489
* @return {Element} The first ancestor that matches the passed criteria, or
2490
* null if none match.
2491
*/
2492
goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {
2493
'use strict';
2494
return goog.dom.getAncestorByTagNameAndClass(
2495
element, null, className, opt_maxSearchSteps);
2496
};
2497
2498
2499
/**
2500
* Walks up the DOM hierarchy returning the first ancestor that passes the
2501
* matcher function.
2502
* @param {Node} element The DOM node to start with.
2503
* @param {function(!Node) : boolean} matcher A function that returns true if
2504
* the passed node matches the desired criteria.
2505
* @param {boolean=} opt_includeNode If true, the node itself is included in
2506
* the search (the first call to the matcher will pass startElement as
2507
* the node to test).
2508
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
2509
* dom.
2510
* @return {Node} DOM node that matched the matcher, or null if there was
2511
* no match.
2512
*/
2513
goog.dom.getAncestor = function(
2514
element, matcher, opt_includeNode, opt_maxSearchSteps) {
2515
'use strict';
2516
if (element && !opt_includeNode) {
2517
element = element.parentNode;
2518
}
2519
var steps = 0;
2520
while (element &&
2521
(opt_maxSearchSteps == null || steps <= opt_maxSearchSteps)) {
2522
goog.asserts.assert(element.name != 'parentNode');
2523
if (matcher(element)) {
2524
return element;
2525
}
2526
element = element.parentNode;
2527
steps++;
2528
}
2529
// Reached the root of the DOM without a match
2530
return null;
2531
};
2532
2533
2534
/**
2535
* Determines the active element in the given document.
2536
* @param {Document} doc The document to look in.
2537
* @return {Element} The active element.
2538
*/
2539
goog.dom.getActiveElement = function(doc) {
2540
'use strict';
2541
// While in an iframe, IE9 will throw "Unspecified error" when accessing
2542
// activeElement.
2543
try {
2544
var activeElement = doc && doc.activeElement;
2545
// While not in an iframe, IE9-11 sometimes gives null.
2546
// While in an iframe, IE11 sometimes returns an empty object.
2547
return activeElement && activeElement.nodeName ? activeElement : null;
2548
} catch (e) {
2549
return null;
2550
}
2551
};
2552
2553
2554
/**
2555
* Gives the current devicePixelRatio.
2556
*
2557
* By default, this is the value of window.devicePixelRatio (which should be
2558
* preferred if present).
2559
*
2560
* If window.devicePixelRatio is not present, the ratio is calculated with
2561
* window.matchMedia, if present. Otherwise, gives 1.0.
2562
*
2563
* Some browsers (including Chrome) consider the browser zoom level in the pixel
2564
* ratio, so the value may change across multiple calls.
2565
*
2566
* @return {number} The number of actual pixels per virtual pixel.
2567
*/
2568
goog.dom.getPixelRatio = function() {
2569
'use strict';
2570
var win = goog.dom.getWindow();
2571
if (win.devicePixelRatio !== undefined) {
2572
return win.devicePixelRatio;
2573
} else if (win.matchMedia) {
2574
// Should be for IE10 and FF6-17 (this basically clamps to lower)
2575
// Note that the order of these statements is important
2576
return goog.dom.matchesPixelRatio_(3) || goog.dom.matchesPixelRatio_(2) ||
2577
goog.dom.matchesPixelRatio_(1.5) || goog.dom.matchesPixelRatio_(1) ||
2578
.75;
2579
}
2580
return 1;
2581
};
2582
2583
2584
/**
2585
* Calculates a mediaQuery to check if the current device supports the
2586
* given actual to virtual pixel ratio.
2587
* @param {number} pixelRatio The ratio of actual pixels to virtual pixels.
2588
* @return {number} pixelRatio if applicable, otherwise 0.
2589
* @private
2590
*/
2591
goog.dom.matchesPixelRatio_ = function(pixelRatio) {
2592
'use strict';
2593
var win = goog.dom.getWindow();
2594
/**
2595
* Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to
2596
* 96dpi.
2597
* @const {number}
2598
*/
2599
var dpiPerDppx = 96;
2600
var query =
2601
// FF16-17
2602
'(min-resolution: ' + pixelRatio + 'dppx),' +
2603
// FF6-15
2604
'(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +
2605
// IE10 (this works for the two browsers above too but I don't want to
2606
// trust the 1:96 fixed ratio magic)
2607
'(min-resolution: ' + (pixelRatio * dpiPerDppx) + 'dpi)';
2608
return win.matchMedia(query).matches ? pixelRatio : 0;
2609
};
2610
2611
2612
/**
2613
* Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a
2614
* type information.
2615
* @param {!HTMLCanvasElement|!OffscreenCanvas} canvas
2616
* @return {!CanvasRenderingContext2D}
2617
*/
2618
goog.dom.getCanvasContext2D = function(canvas) {
2619
'use strict';
2620
return /** @type {!CanvasRenderingContext2D} */ (canvas.getContext('2d'));
2621
};
2622
2623
2624
2625
/**
2626
* Create an instance of a DOM helper with a new document object.
2627
* @param {Document=} opt_document Document object to associate with this
2628
* DOM helper.
2629
* @constructor
2630
*/
2631
goog.dom.DomHelper = function(opt_document) {
2632
'use strict';
2633
/**
2634
* Reference to the document object to use
2635
* @type {!Document}
2636
* @private
2637
*/
2638
this.document_ = opt_document || goog.global.document || document;
2639
};
2640
2641
2642
/**
2643
* Gets the dom helper object for the document where the element resides.
2644
* @param {Node=} opt_node If present, gets the DomHelper for this node.
2645
* @return {!goog.dom.DomHelper} The DomHelper.
2646
*/
2647
goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
2648
2649
2650
/**
2651
* Sets the document object.
2652
* @param {!Document} document Document object.
2653
*/
2654
goog.dom.DomHelper.prototype.setDocument = function(document) {
2655
'use strict';
2656
this.document_ = document;
2657
};
2658
2659
2660
/**
2661
* Gets the document object being used by the dom library.
2662
* @return {!Document} Document object.
2663
*/
2664
goog.dom.DomHelper.prototype.getDocument = function() {
2665
'use strict';
2666
return this.document_;
2667
};
2668
2669
2670
/**
2671
* Alias for `getElementById`. If a DOM node is passed in then we just
2672
* return that.
2673
* @param {string|Element} element Element ID or a DOM node.
2674
* @return {Element} The element with the given ID, or the node passed in.
2675
*/
2676
goog.dom.DomHelper.prototype.getElement = function(element) {
2677
'use strict';
2678
return goog.dom.getElementHelper_(this.document_, element);
2679
};
2680
2681
2682
/**
2683
* Gets an element by id, asserting that the element is found.
2684
*
2685
* This is used when an element is expected to exist, and should fail with
2686
* an assertion error if it does not (if assertions are enabled).
2687
*
2688
* @param {string} id Element ID.
2689
* @return {!Element} The element with the given ID, if it exists.
2690
*/
2691
goog.dom.DomHelper.prototype.getRequiredElement = function(id) {
2692
'use strict';
2693
return goog.dom.getRequiredElementHelper_(this.document_, id);
2694
};
2695
2696
2697
/**
2698
* Alias for `getElement`.
2699
* @param {string|Element} element Element ID or a DOM node.
2700
* @return {Element} The element with the given ID, or the node passed in.
2701
* @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.
2702
*/
2703
goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
2704
2705
2706
/**
2707
* Gets elements by tag name.
2708
* @param {!goog.dom.TagName<T>} tagName
2709
* @param {(!Document|!Element)=} opt_parent Parent element or document where to
2710
* look for elements. Defaults to document of this DomHelper.
2711
* @return {!NodeList<R>} List of elements. The members of the list are
2712
* {!Element} if tagName is not a member of goog.dom.TagName or more
2713
* specific types if it is (e.g. {!HTMLAnchorElement} for
2714
* goog.dom.TagName.A).
2715
* @template T
2716
* @template R := cond(isUnknown(T), 'Element', T) =:
2717
*/
2718
goog.dom.DomHelper.prototype.getElementsByTagName = function(
2719
tagName, opt_parent) {
2720
'use strict';
2721
var parent = opt_parent || this.document_;
2722
return parent.getElementsByTagName(String(tagName));
2723
};
2724
2725
2726
/**
2727
* Looks up elements by both tag and class name, using browser native functions
2728
* (`querySelectorAll`, `getElementsByTagName` or
2729
* `getElementsByClassName`) where possible. The returned array is a live
2730
* NodeList or a static list depending on the code path taken.
2731
*
2732
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name or * for all
2733
* tags.
2734
* @param {?string=} opt_class Optional class name.
2735
* @param {(Document|Element)=} opt_el Optional element to look in.
2736
* @return {!IArrayLike<R>} Array-like list of elements (only a length property
2737
* and numerical indices are guaranteed to exist). The members of the array
2738
* are {!Element} if opt_tag is not a member of goog.dom.TagName or more
2739
* specific types if it is (e.g. {!HTMLAnchorElement} for
2740
* goog.dom.TagName.A).
2741
* @template T
2742
* @template R := cond(isUnknown(T), 'Element', T) =:
2743
*/
2744
goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(
2745
opt_tag, opt_class, opt_el) {
2746
'use strict';
2747
return goog.dom.getElementsByTagNameAndClass_(
2748
this.document_, opt_tag, opt_class, opt_el);
2749
};
2750
2751
2752
/**
2753
* Gets the first element matching the tag and the class.
2754
*
2755
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
2756
* @param {?string=} opt_class Optional class name.
2757
* @param {(Document|Element)=} opt_el Optional element to look in.
2758
* @return {?R} Reference to a DOM node. The return type is {?Element} if
2759
* tagName is a string or a more specific type if it is a member of
2760
* goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
2761
* @template T
2762
* @template R := cond(isUnknown(T), 'Element', T) =:
2763
*/
2764
goog.dom.DomHelper.prototype.getElementByTagNameAndClass = function(
2765
opt_tag, opt_class, opt_el) {
2766
'use strict';
2767
return goog.dom.getElementByTagNameAndClass_(
2768
this.document_, opt_tag, opt_class, opt_el);
2769
};
2770
2771
2772
/**
2773
* Returns an array of all the elements with the provided className.
2774
* @param {string} className the name of the class to look for.
2775
* @param {Element|Document=} opt_el Optional element to look in.
2776
* @return {!IArrayLike<!Element>} The items found with the class name provided.
2777
*/
2778
goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
2779
'use strict';
2780
var doc = opt_el || this.document_;
2781
return goog.dom.getElementsByClass(className, doc);
2782
};
2783
2784
2785
/**
2786
* Returns the first element we find matching the provided class name.
2787
* @param {string} className the name of the class to look for.
2788
* @param {(Element|Document)=} opt_el Optional element to look in.
2789
* @return {Element} The first item found with the class name provided.
2790
*/
2791
goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
2792
'use strict';
2793
var doc = opt_el || this.document_;
2794
return goog.dom.getElementByClass(className, doc);
2795
};
2796
2797
2798
/**
2799
* Ensures an element with the given className exists, and then returns the
2800
* first element with the provided className.
2801
* @param {string} className the name of the class to look for.
2802
* @param {(!Element|!Document)=} opt_root Optional element or document to look
2803
* in.
2804
* @return {!Element} The first item found with the class name provided.
2805
* @throws {goog.asserts.AssertionError} Thrown if no element is found.
2806
*/
2807
goog.dom.DomHelper.prototype.getRequiredElementByClass = function(
2808
className, opt_root) {
2809
'use strict';
2810
var root = opt_root || this.document_;
2811
return goog.dom.getRequiredElementByClass(className, root);
2812
};
2813
2814
2815
/**
2816
* Alias for `getElementsByTagNameAndClass`.
2817
* @deprecated Use DomHelper getElementsByTagNameAndClass.
2818
*
2819
* @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
2820
* @param {?string=} opt_class Optional class name.
2821
* @param {Element=} opt_el Optional element to look in.
2822
* @return {!IArrayLike<R>} Array-like list of elements (only a length property
2823
* and numerical indices are guaranteed to exist). The members of the array
2824
* are {!Element} if opt_tag is a string or more specific types if it is
2825
* a member of goog.dom.TagName (e.g. {!HTMLAnchorElement} for
2826
* goog.dom.TagName.A).
2827
* @template T
2828
* @template R := cond(isUnknown(T), 'Element', T) =:
2829
*/
2830
goog.dom.DomHelper.prototype.$$ =
2831
goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
2832
2833
2834
/**
2835
* Sets a number of properties on a node.
2836
* @param {Element} element DOM node to set properties on.
2837
* @param {Object} properties Hash of property:value pairs.
2838
*/
2839
goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
2840
2841
2842
/**
2843
* Gets the dimensions of the viewport.
2844
* @param {Window=} opt_window Optional window element to test. Defaults to
2845
* the window of the Dom Helper.
2846
* @return {!goog.math.Size} Object with values 'width' and 'height'.
2847
*/
2848
goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
2849
'use strict';
2850
// TODO(arv): This should not take an argument. That breaks the rule of a
2851
// a DomHelper representing a single frame/window/document.
2852
return goog.dom.getViewportSize(opt_window || this.getWindow());
2853
};
2854
2855
2856
/**
2857
* Calculates the height of the document.
2858
*
2859
* @return {number} The height of the document.
2860
*/
2861
goog.dom.DomHelper.prototype.getDocumentHeight = function() {
2862
'use strict';
2863
return goog.dom.getDocumentHeight_(this.getWindow());
2864
};
2865
2866
2867
/**
2868
* Typedef for use with goog.dom.createDom and goog.dom.append.
2869
* @typedef {Object|string|Array|NodeList}
2870
*/
2871
goog.dom.Appendable;
2872
2873
2874
/**
2875
* Returns a dom node with a set of attributes. This function accepts varargs
2876
* for subsequent nodes to be added. Subsequent nodes will be added to the
2877
* first node as childNodes.
2878
*
2879
* So:
2880
* <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),
2881
* createDom(goog.dom.TagName.P));</code> would return a div with two child
2882
* paragraphs
2883
*
2884
* An easy way to move all child nodes of an existing element to a new parent
2885
* element is:
2886
* <code>createDom(goog.dom.TagName.DIV, null, oldElement.childNodes);</code>
2887
* which will remove all child nodes from the old element and add them as
2888
* child nodes of the new DIV.
2889
*
2890
* @param {string|!goog.dom.TagName<T>} tagName Tag to create.
2891
* @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
2892
* of name-value pairs for attributes. If a string, then this is the
2893
* className of the new element. If an array, the elements will be joined
2894
* together as the className of the new element.
2895
* @param {...(goog.dom.Appendable|undefined)} var_args Further DOM nodes or
2896
* strings for text nodes. If one of the var_args is an array or
2897
* NodeList, its elements will be added as childNodes instead.
2898
* @return {R} Reference to a DOM node. The return type is {!Element} if tagName
2899
* is a string or a more specific type if it is a member of
2900
* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
2901
* @template T
2902
* @template R := cond(isUnknown(T), 'Element', T) =:
2903
*/
2904
goog.dom.DomHelper.prototype.createDom = function(
2905
tagName, opt_attributes, var_args) {
2906
'use strict';
2907
return goog.dom.createDom_(this.document_, arguments);
2908
};
2909
2910
2911
/**
2912
* Alias for `createDom`.
2913
* @param {string|!goog.dom.TagName<T>} tagName Tag to create.
2914
* @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
2915
* of name-value pairs for attributes. If a string, then this is the
2916
* className of the new element. If an array, the elements will be joined
2917
* together as the className of the new element.
2918
* @param {...(goog.dom.Appendable|undefined)} var_args Further DOM nodes or
2919
* strings for text nodes. If one of the var_args is an array, its children
2920
* will be added as childNodes instead.
2921
* @return {R} Reference to a DOM node. The return type is {!Element} if tagName
2922
* is a string or a more specific type if it is a member of
2923
* goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
2924
* @template T
2925
* @template R := cond(isUnknown(T), 'Element', T) =:
2926
* @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.
2927
*/
2928
goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
2929
2930
2931
/**
2932
* Creates a new element.
2933
* @param {string|!goog.dom.TagName<T>} name Tag to create.
2934
* @return {R} The new element. The return type is {!Element} if name is
2935
* a string or a more specific type if it is a member of goog.dom.TagName
2936
* (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
2937
* @template T
2938
* @template R := cond(isUnknown(T), 'Element', T) =:
2939
*/
2940
goog.dom.DomHelper.prototype.createElement = function(name) {
2941
'use strict';
2942
return goog.dom.createElement_(this.document_, name);
2943
};
2944
2945
2946
/**
2947
* Creates a new text node.
2948
* @param {number|string} content Content.
2949
* @return {!Text} The new text node.
2950
*/
2951
goog.dom.DomHelper.prototype.createTextNode = function(content) {
2952
'use strict';
2953
return this.document_.createTextNode(String(content));
2954
};
2955
2956
2957
/**
2958
* Create a table.
2959
* @param {number} rows The number of rows in the table. Must be >= 1.
2960
* @param {number} columns The number of columns in the table. Must be >= 1.
2961
* @param {boolean=} opt_fillWithNbsp If true, fills table entries with
2962
* `goog.string.Unicode.NBSP` characters.
2963
* @return {!HTMLElement} The created table.
2964
*/
2965
goog.dom.DomHelper.prototype.createTable = function(
2966
rows, columns, opt_fillWithNbsp) {
2967
'use strict';
2968
return goog.dom.createTable_(
2969
this.document_, rows, columns, !!opt_fillWithNbsp);
2970
};
2971
2972
2973
/**
2974
* Converts an HTML into a node or a document fragment. A single Node is used if
2975
* `html` only generates a single node. If `html` generates multiple
2976
* nodes then these are put inside a `DocumentFragment`. This is a safe
2977
* version of `goog.dom.DomHelper#htmlToDocumentFragment` which is now
2978
* deleted.
2979
* @param {!goog.html.SafeHtml} html The HTML markup to convert.
2980
* @return {!Node} The resulting node.
2981
*/
2982
goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {
2983
'use strict';
2984
return goog.dom.safeHtmlToNode_(this.document_, html);
2985
};
2986
2987
2988
/**
2989
* Returns true if the browser is in "CSS1-compatible" (standards-compliant)
2990
* mode, false otherwise.
2991
* @return {boolean} True if in CSS1-compatible mode.
2992
*/
2993
goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
2994
'use strict';
2995
return goog.dom.isCss1CompatMode_(this.document_);
2996
};
2997
2998
2999
/**
3000
* Gets the window object associated with the document.
3001
* @return {!Window} The window associated with the given document.
3002
*/
3003
goog.dom.DomHelper.prototype.getWindow = function() {
3004
'use strict';
3005
return goog.dom.getWindow_(this.document_);
3006
};
3007
3008
3009
/**
3010
* Gets the document scroll element.
3011
* @return {!Element} Scrolling element.
3012
*/
3013
goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
3014
'use strict';
3015
return goog.dom.getDocumentScrollElement_(this.document_);
3016
};
3017
3018
3019
/**
3020
* Gets the document scroll distance as a coordinate object.
3021
* @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.
3022
*/
3023
goog.dom.DomHelper.prototype.getDocumentScroll = function() {
3024
'use strict';
3025
return goog.dom.getDocumentScroll_(this.document_);
3026
};
3027
3028
3029
/**
3030
* Determines the active element in the given document.
3031
* @param {Document=} opt_doc The document to look in.
3032
* @return {Element} The active element.
3033
*/
3034
goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {
3035
'use strict';
3036
return goog.dom.getActiveElement(opt_doc || this.document_);
3037
};
3038
3039
3040
/**
3041
* Appends a child to a node.
3042
* @param {Node} parent Parent.
3043
* @param {Node} child Child.
3044
*/
3045
goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
3046
3047
3048
/**
3049
* Appends a node with text or other nodes.
3050
* @param {!Node} parent The node to append nodes to.
3051
* @param {...goog.dom.Appendable} var_args The things to append to the node.
3052
* If this is a Node it is appended as is.
3053
* If this is a string then a text node is appended.
3054
* If this is an array like object then fields 0 to length - 1 are appended.
3055
*/
3056
goog.dom.DomHelper.prototype.append = goog.dom.append;
3057
3058
3059
/**
3060
* Determines if the given node can contain children, intended to be used for
3061
* HTML generation.
3062
*
3063
* @param {Node} node The node to check.
3064
* @return {boolean} Whether the node can contain children.
3065
*/
3066
goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;
3067
3068
3069
/**
3070
* Removes all the child nodes on a DOM node.
3071
* @param {Node} node Node to remove children from.
3072
*/
3073
goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
3074
3075
3076
/**
3077
* Inserts a new node before an existing reference node (i.e., as the previous
3078
* sibling). If the reference node has no parent, then does nothing.
3079
* @param {Node} newNode Node to insert.
3080
* @param {Node} refNode Reference node to insert before.
3081
*/
3082
goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
3083
3084
3085
/**
3086
* Inserts a new node after an existing reference node (i.e., as the next
3087
* sibling). If the reference node has no parent, then does nothing.
3088
* @param {Node} newNode Node to insert.
3089
* @param {Node} refNode Reference node to insert after.
3090
*/
3091
goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
3092
3093
3094
/**
3095
* Insert a child at a given index. If index is larger than the number of child
3096
* nodes that the parent currently has, the node is inserted as the last child
3097
* node.
3098
* @param {Element} parent The element into which to insert the child.
3099
* @param {Node} child The element to insert.
3100
* @param {number} index The index at which to insert the new child node. Must
3101
* not be negative.
3102
*/
3103
goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;
3104
3105
3106
/**
3107
* Removes a node from its parent.
3108
* @param {Node} node The node to remove.
3109
* @return {Node} The node removed if removed; else, null.
3110
*/
3111
goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
3112
3113
3114
/**
3115
* Replaces a node in the DOM tree. Will do nothing if `oldNode` has no
3116
* parent.
3117
* @param {Node} newNode Node to insert.
3118
* @param {Node} oldNode Node to replace.
3119
*/
3120
goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
3121
3122
3123
/**
3124
* Replaces child nodes of `target` with child nodes of `source`. This is
3125
* roughly equivalent to `target.innerHTML = source.innerHTML` which is not
3126
* compatible with Trusted Types.
3127
* @param {?Node} target Node to clean and replace its children.
3128
* @param {?Node} source Node to get the children from. The nodes will be cloned
3129
* so they will stay in source.
3130
*/
3131
goog.dom.DomHelper.prototype.copyContents = goog.dom.copyContents;
3132
3133
3134
/**
3135
* Flattens an element. That is, removes it and replace it with its children.
3136
* @param {Element} element The element to flatten.
3137
* @return {Element|undefined} The original element, detached from the document
3138
* tree, sans children, or undefined if the element was already not in the
3139
* document.
3140
*/
3141
goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
3142
3143
3144
/**
3145
* Returns an array containing just the element children of the given element.
3146
* @param {Element} element The element whose element children we want.
3147
* @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list
3148
* of just the element children of the given element.
3149
*/
3150
goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;
3151
3152
3153
/**
3154
* Returns the first child node that is an element.
3155
* @param {Node} node The node to get the first child element of.
3156
* @return {Element} The first child node of `node` that is an element.
3157
*/
3158
goog.dom.DomHelper.prototype.getFirstElementChild =
3159
goog.dom.getFirstElementChild;
3160
3161
3162
/**
3163
* Returns the last child node that is an element.
3164
* @param {Node} node The node to get the last child element of.
3165
* @return {Element} The last child node of `node` that is an element.
3166
*/
3167
goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
3168
3169
3170
/**
3171
* Returns the first next sibling that is an element.
3172
* @param {Node} node The node to get the next sibling element of.
3173
* @return {Element} The next sibling of `node` that is an element.
3174
*/
3175
goog.dom.DomHelper.prototype.getNextElementSibling =
3176
goog.dom.getNextElementSibling;
3177
3178
3179
/**
3180
* Returns the first previous sibling that is an element.
3181
* @param {Node} node The node to get the previous sibling element of.
3182
* @return {Element} The first previous sibling of `node` that is
3183
* an element.
3184
*/
3185
goog.dom.DomHelper.prototype.getPreviousElementSibling =
3186
goog.dom.getPreviousElementSibling;
3187
3188
3189
/**
3190
* Returns the next node in source order from the given node.
3191
* @param {Node} node The node.
3192
* @return {Node} The next node in the DOM tree, or null if this was the last
3193
* node.
3194
*/
3195
goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
3196
3197
3198
/**
3199
* Returns the previous node in source order from the given node.
3200
* @param {Node} node The node.
3201
* @return {Node} The previous node in the DOM tree, or null if this was the
3202
* first node.
3203
*/
3204
goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
3205
3206
3207
/**
3208
* Whether the object looks like a DOM node.
3209
* @param {?} obj The object being tested for node likeness.
3210
* @return {boolean} Whether the object looks like a DOM node.
3211
*/
3212
goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
3213
3214
3215
/**
3216
* Whether the object looks like an Element.
3217
* @param {?} obj The object being tested for Element likeness.
3218
* @return {boolean} Whether the object looks like an Element.
3219
*/
3220
goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;
3221
3222
3223
/**
3224
* Returns true if the specified value is a Window object. This includes the
3225
* global window for HTML pages, and iframe windows.
3226
* @param {?} obj Variable to test.
3227
* @return {boolean} Whether the variable is a window.
3228
*/
3229
goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;
3230
3231
3232
/**
3233
* Returns an element's parent, if it's an Element.
3234
* @param {Element} element The DOM element.
3235
* @return {Element} The parent, or null if not an Element.
3236
*/
3237
goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;
3238
3239
3240
/**
3241
* Whether a node contains another node.
3242
* @param {Node} parent The node that should contain the other node.
3243
* @param {Node} descendant The node to test presence of.
3244
* @return {boolean} Whether the parent node contains the descendant node.
3245
*/
3246
goog.dom.DomHelper.prototype.contains = goog.dom.contains;
3247
3248
3249
/**
3250
* Compares the document order of two nodes, returning 0 if they are the same
3251
* node, a negative number if node1 is before node2, and a positive number if
3252
* node2 is before node1. Note that we compare the order the tags appear in the
3253
* document so in the tree <b><i>text</i></b> the B node is considered to be
3254
* before the I node.
3255
*
3256
* @param {Node} node1 The first node to compare.
3257
* @param {Node} node2 The second node to compare.
3258
* @return {number} 0 if the nodes are the same node, a negative number if node1
3259
* is before node2, and a positive number if node2 is before node1.
3260
*/
3261
goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;
3262
3263
3264
/**
3265
* Find the deepest common ancestor of the given nodes.
3266
* @param {...Node} var_args The nodes to find a common ancestor of.
3267
* @return {Node} The common ancestor of the nodes, or null if there is none.
3268
* null will only be returned if two or more of the nodes are from different
3269
* documents.
3270
*/
3271
goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;
3272
3273
3274
/**
3275
* Returns the owner document for a node.
3276
* @param {Node} node The node to get the document for.
3277
* @return {!Document} The document owning the node.
3278
*/
3279
goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
3280
3281
3282
/**
3283
* Cross browser function for getting the document element of an iframe.
3284
* @param {Element} iframe Iframe element.
3285
* @return {!Document} The frame content document.
3286
*/
3287
goog.dom.DomHelper.prototype.getFrameContentDocument =
3288
goog.dom.getFrameContentDocument;
3289
3290
3291
/**
3292
* Cross browser function for getting the window of a frame or iframe.
3293
* @param {Element} frame Frame element.
3294
* @return {Window} The window associated with the given frame.
3295
*/
3296
goog.dom.DomHelper.prototype.getFrameContentWindow =
3297
goog.dom.getFrameContentWindow;
3298
3299
3300
/**
3301
* Sets the text content of a node, with cross-browser support.
3302
* @param {Node} node The node to change the text content of.
3303
* @param {string|number} text The value that should replace the node's content.
3304
*/
3305
goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
3306
3307
3308
/**
3309
* Gets the outerHTML of a node, which islike innerHTML, except that it
3310
* actually contains the HTML of the node itself.
3311
* @param {Element} element The element to get the HTML of.
3312
* @return {string} The outerHTML of the given element.
3313
*/
3314
goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;
3315
3316
3317
/**
3318
* Finds the first descendant node that matches the filter function. This does
3319
* a depth first search.
3320
* @param {Node} root The root of the tree to search.
3321
* @param {function(Node) : boolean} p The filter function.
3322
* @return {Node|undefined} The found node or undefined if none is found.
3323
*/
3324
goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
3325
3326
3327
/**
3328
* Finds all the descendant nodes that matches the filter function. This does a
3329
* depth first search.
3330
* @param {Node} root The root of the tree to search.
3331
* @param {function(Node) : boolean} p The filter function.
3332
* @return {Array<Node>} The found nodes or an empty array if none are found.
3333
*/
3334
goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
3335
3336
3337
/**
3338
* Returns true if the element has a tab index that allows it to receive
3339
* keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
3340
* natively support keyboard focus, even if they have no tab index.
3341
* @param {!Element} element Element to check.
3342
* @return {boolean} Whether the element has a tab index that allows keyboard
3343
* focus.
3344
*/
3345
goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;
3346
3347
3348
/**
3349
* Enables or disables keyboard focus support on the element via its tab index.
3350
* Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
3351
* (or elements that natively support keyboard focus, like form elements) can
3352
* receive keyboard focus. See http://go/tabindex for more info.
3353
* @param {Element} element Element whose tab index is to be changed.
3354
* @param {boolean} enable Whether to set or remove a tab index on the element
3355
* that supports keyboard focus.
3356
*/
3357
goog.dom.DomHelper.prototype.setFocusableTabIndex =
3358
goog.dom.setFocusableTabIndex;
3359
3360
3361
/**
3362
* Returns true if the element can be focused, i.e. it has a tab index that
3363
* allows it to receive keyboard focus (tabIndex >= 0), or it is an element
3364
* that natively supports keyboard focus.
3365
* @param {!Element} element Element to check.
3366
* @return {boolean} Whether the element allows keyboard focus.
3367
*/
3368
goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;
3369
3370
3371
/**
3372
* Returns the text contents of the current node, without markup. New lines are
3373
* stripped and whitespace is collapsed, such that each character would be
3374
* visible.
3375
*
3376
* In browsers that support it, innerText is used. Other browsers attempt to
3377
* simulate it via node traversal. Line breaks are canonicalized in IE.
3378
*
3379
* @param {Node} node The node from which we are getting content.
3380
* @return {string} The text content.
3381
*/
3382
goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
3383
3384
3385
/**
3386
* Returns the text length of the text contained in a node, without markup. This
3387
* is equivalent to the selection length if the node was selected, or the number
3388
* of cursor movements to traverse the node. Images & BRs take one space. New
3389
* lines are ignored.
3390
*
3391
* @param {Node} node The node whose text content length is being calculated.
3392
* @return {number} The length of `node`'s text content.
3393
*/
3394
goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
3395
3396
3397
/**
3398
* Returns the text offset of a node relative to one of its ancestors. The text
3399
* length is the same as the length calculated by
3400
* `goog.dom.getNodeTextLength`.
3401
*
3402
* @param {Node} node The node whose offset is being calculated.
3403
* @param {Node=} opt_offsetParent Defaults to the node's owner document's body.
3404
* @return {number} The text offset.
3405
*/
3406
goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
3407
3408
3409
/**
3410
* Returns the node at a given offset in a parent node. If an object is
3411
* provided for the optional third parameter, the node and the remainder of the
3412
* offset will stored as properties of this object.
3413
* @param {Node} parent The parent node.
3414
* @param {number} offset The offset into the parent node.
3415
* @param {Object=} opt_result Object to be used to store the return value. The
3416
* return value will be stored in the form {node: Node, remainder: number}
3417
* if this object is provided.
3418
* @return {Node} The node at the given offset.
3419
*/
3420
goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;
3421
3422
3423
/**
3424
* Returns true if the object is a `NodeList`. To qualify as a NodeList,
3425
* the object must have a numeric length property and an item function (which
3426
* has type 'string' on IE for some reason).
3427
* @param {Object} val Object to test.
3428
* @return {boolean} Whether the object is a NodeList.
3429
*/
3430
goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;
3431
3432
3433
/**
3434
* Walks up the DOM hierarchy returning the first ancestor that has the passed
3435
* tag name and/or class name. If the passed element matches the specified
3436
* criteria, the element itself is returned.
3437
* @param {Node} element The DOM node to start with.
3438
* @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or
3439
* null/undefined to match only based on class name).
3440
* @param {?string=} opt_class The class name to match (or null/undefined to
3441
* match only based on tag name).
3442
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
3443
* dom.
3444
* @return {?R} The first ancestor that matches the passed criteria, or
3445
* null if no match is found. The return type is {?Element} if opt_tag is
3446
* not a member of goog.dom.TagName or a more specific type if it is (e.g.
3447
* {?HTMLAnchorElement} for goog.dom.TagName.A).
3448
* @template T
3449
* @template R := cond(isUnknown(T), 'Element', T) =:
3450
*/
3451
goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =
3452
goog.dom.getAncestorByTagNameAndClass;
3453
3454
3455
/**
3456
* Walks up the DOM hierarchy returning the first ancestor that has the passed
3457
* class name. If the passed element matches the specified criteria, the
3458
* element itself is returned.
3459
* @param {Node} element The DOM node to start with.
3460
* @param {string} class The class name to match.
3461
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
3462
* dom.
3463
* @return {Element} The first ancestor that matches the passed criteria, or
3464
* null if none match.
3465
*/
3466
goog.dom.DomHelper.prototype.getAncestorByClass = goog.dom.getAncestorByClass;
3467
3468
3469
/**
3470
* Walks up the DOM hierarchy returning the first ancestor that passes the
3471
* matcher function.
3472
* @param {Node} element The DOM node to start with.
3473
* @param {function(Node) : boolean} matcher A function that returns true if the
3474
* passed node matches the desired criteria.
3475
* @param {boolean=} opt_includeNode If true, the node itself is included in
3476
* the search (the first call to the matcher will pass startElement as
3477
* the node to test).
3478
* @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
3479
* dom.
3480
* @return {Node} DOM node that matched the matcher, or null if there was
3481
* no match.
3482
*/
3483
goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;
3484
3485
3486
/**
3487
* Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a
3488
* type information.
3489
* @param {!HTMLCanvasElement} canvas
3490
* @return {!CanvasRenderingContext2D}
3491
*/
3492
goog.dom.DomHelper.prototype.getCanvasContext2D = goog.dom.getCanvasContext2D;
3493
3494