Skip to content

Commit 80e84ba

Browse files
authored
Merged dev into fix/link-router-to (bootstrap-vue#1018)
* [carousel] Use dom utils * feat: dom utility methods (bootstrap-vue#1013) * feat: dom utility methods * Update index.js * [tooltip.js] Use dom utils * [dropdown.js] Use dom utils * [scrollspy.js] Use dom utils * feat: Use dom utils (bootstrap-vue#1017) * feat: dom utility methods * Update index.js * [tooltip.js] Use dom utils * [dropdown.js] Use dom utils * [scrollspy.js] Use dom utils * feat(array mixin): Add polyfill for Array.find for IE * [modal] use dom utils * [popover.vue] Use dom utils * [tooltip.vue] Use dom utils
1 parent 14435bd commit 80e84ba

File tree

10 files changed

+155
-155
lines changed

10 files changed

+155
-155
lines changed

lib/classes/tooltip.js

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Popper from 'popper.js';
22
import { assign, keys } from '../utils/object';
33
import { from as arrayFrom } from '../utils/array';
4+
import { closest, isVisible, isDisabled } from '../utils/dom';
45
import BvEvent from './BvEvent';
56

67
const inBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
@@ -80,38 +81,6 @@ function generateId(name) {
8081
return `__BV_${name}_${NEXTID++}__`;
8182
}
8283

83-
// Determine if an element is visible. Faster than CSS checks
84-
function elVisible(el) {
85-
return el &&
86-
document.body.contains(el) &&
87-
el.offsetParent !== null &&
88-
(el.offsetWidth > 0 || el.offsetHeight > 0);
89-
}
90-
91-
// Determine if an element is disabled
92-
function elDisabled(el) {
93-
return !el || el.disabled || el.classList.contains('disabled') || Boolean(el.getAttribute('disabled'));
94-
}
95-
96-
/*
97-
* Polyfill for Element.closest() for IE :(
98-
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
99-
*/
100-
if (inBrowser && window.Element && !Element.prototype.closest) {
101-
Element.prototype.closest = function (s) {
102-
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
103-
let el = this;
104-
let i;
105-
do {
106-
i = matches.length;
107-
// eslint-disable-next-line no-empty
108-
while (--i >= 0 && matches.item(i) !== el) {
109-
}
110-
} while ((i < 0) && (el = el.parentElement));
111-
return el;
112-
};
113-
}
114-
11584
/*
11685
* ToolTip Class definition
11786
*/
@@ -311,7 +280,7 @@ class ToolTip {
311280
if (on) {
312281
this.$visibleInterval = setInterval(() => {
313282
const tip = this.getTipElement();
314-
if (tip && !elVisible(this.$element) && tip.classList.contains(ClassName.SHOW)) {
283+
if (tip && !isVisible(this.$element) && tip.classList.contains(ClassName.SHOW)) {
315284
// Element is no longer visible, so force-hide the tooltip
316285
this.forceHide();
317286
}
@@ -610,7 +579,7 @@ class ToolTip {
610579

611580
handleEvent(e) {
612581
// This special method allows us to use "this" as the event handlers
613-
if (elDisabled(this.$element)) {
582+
if (isDisabled(this.$element)) {
614583
// If disabled, don't do anything. Note: if tip is shown before element gets
615584
// disabled, then tip not close until no longer disabled or forcefully closed.
616585
return;
@@ -646,7 +615,7 @@ class ToolTip {
646615
}
647616

648617
setModalListener(on) {
649-
const modal = this.$element.closest(MODAL_CLASS);
618+
const modal = closest(MODAL_CLASS, this.$element);
650619
if (!modal) {
651620
// If we are not in a modal, don't worry. be happy
652621
return;

lib/components/carousel.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<script>
7979
import { from as arrayFrom } from '../utils/array';
8080
import { observeDom } from '../utils';
81+
import { selectAll } from '../utils/dom';
8182
import { idMixin } from '../mixins';
8283
8384
// Slide directional classes
@@ -257,7 +258,7 @@
257258
this.pause();
258259
259260
// Get all slides as DOM elements
260-
this.slides = arrayFrom(this.$refs.inner.querySelectorAll('.carousel-item'));
261+
this.slides = selectAll('.carousel-item', this.$refs.inner);
261262
262263
const numSlides = this.slides.length;
263264

lib/components/modal.vue

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@
9393
<script>
9494
import bBtn from './button';
9595
import { listenOnRootMixin } from '../mixins';
96-
import { from as arrayFrom } from '../utils/array'
96+
import { from as arrayFrom, arrayFind } from '../utils/array';
97+
import { isElement, isVisible, selectAll, select } from '../utils/dom';
9798
9899
const FOCUS_SELECTOR = [
99100
'button:not([disabled])',
@@ -104,27 +105,12 @@
104105
'[tabindex]:not([disabled]):not(.disabled)'
105106
].join(',');
106107
107-
// Determine if an HTML element is visible - Faster than CSS check
108-
function isVisible(el) {
109-
return el && (el.offsetWidth > 0 || el.offsetHeight > 0);
110-
}
111-
112108
// Find the first visible element contained in a given root element
113109
function findFirstVisible(root, selector) {
114-
if (!root || !root.querySelectorAll || !selector) {
110+
if (!isElement(root) || !selector) {
115111
return null;
116112
}
117-
let els = arrayFrom(root.querySelectorAll(selector));
118-
119-
// IE 10 & 11 do not support native array.find()
120-
// So we try native find first, then fall back to a loop
121-
let el = els.find ? els.find(el => isVisible(el)) : null;
122-
for (let i = 0; !el && i < els.length; i++) {
123-
if (isVisible(els[i])) {
124-
el = els[i];
125-
}
126-
}
127-
return el;
113+
return arrayFind(selectAll(selector, root), isVisible) || null;
128114
}
129115
130116
export default {
@@ -143,7 +129,7 @@
143129
computed: {
144130
body() {
145131
if (typeof document !== 'undefined') {
146-
return document.querySelector('body');
132+
return document.body;
147133
}
148134
}
149135
},
@@ -357,7 +343,7 @@
357343
if (el) {
358344
if (typeof el === 'string') {
359345
// CSS Selector
360-
el = document.querySelector(el);
346+
el = select(el);
361347
}
362348
if (el && el.$el && typeof el.$el.focus === 'function') {
363349
// Component vm reference

lib/components/popover.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import PopOver from '../classes/popover';
1111
import { isArray } from '../utils/array';
1212
import { assign } from '../utils/object';
13+
import { isElement } from '../utils/dom';
1314
import { observeDom, warn } from '../utils';
1415
1516
export default {
@@ -160,7 +161,7 @@
160161
} else if (typeof target === 'object' && target.$el) {
161162
// Component reference
162163
return target.$el;
163-
} else if (typeof target === 'object' && target.tagName) {
164+
} else if (typeof target === 'object' && isElement(target)) {
164165
// Element reference
165166
return target;
166167
}

lib/components/tooltip.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import ToolTip from '../classes/tooltip';
1010
import { isArray } from '../utils/array';
1111
import { assign } from '../utils/object';
12+
import { isElement } from '../utils/dom';
1213
import { observeDom, warn } from '../utils';
1314
1415
export default {
@@ -150,7 +151,7 @@
150151
} else if (typeof target === 'object' && target.$el) {
151152
// Component reference
152153
return target.$el;
153-
} else if (typeof target === 'object' && target.tagName) {
154+
} else if (typeof target === 'object' && isElement(target)) {
154155
// Element reference
155156
return target;
156157
}

lib/directives/scrollspy.js

Lines changed: 10 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
11
import { isArray, from as arrayFrom } from '../utils/array';
22
import { assign, keys } from '../utils/object';
3+
import { isElement, closest, selectAll, select } from '../utils/dom';
4+
35
const inBrowser = typeof window !== 'undefined';
46
const isServer = !inBrowser;
57

6-
/*
7-
* Polyfill for Element.closest() for IE :(
8-
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
9-
*/
10-
11-
if (inBrowser && window.Element && !Element.prototype.closest) {
12-
Element.prototype.closest = function (s) {
13-
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
14-
let el = this;
15-
let i;
16-
do {
17-
i = matches.length;
18-
// eslint-disable-next-line no-empty
19-
while (--i >= 0 && matches.item(i) !== el) {
20-
}
21-
} while ((i < 0) && (el = el.parentElement));
22-
return el;
23-
};
24-
}
25-
268
/*
279
* Constants / Defaults
2810
*/
@@ -71,42 +53,6 @@ const OffsetMethod = {
7153
POSITION: 'position'
7254
};
7355

74-
/*
75-
* DOM Utility Methods
76-
*/
77-
78-
function isElement(obj) {
79-
return obj.nodeType;
80-
}
81-
82-
// Wrapper for Element.closest to emulate jQuery's closest (sorta)
83-
function closest(element, selector) {
84-
const el = element.closest(selector);
85-
return el === element ? null : el;
86-
}
87-
88-
// Query Selector All wrapper
89-
function $QSA(selector, element) {
90-
if (!element) {
91-
element = document;
92-
}
93-
if (!isElement(element)) {
94-
return [];
95-
}
96-
return arrayFrom(element.querySelectorAll(selector));
97-
}
98-
99-
// Query Selector wrapper
100-
function $QS(selector, element) {
101-
if (!element) {
102-
element = document;
103-
}
104-
if (!isElement(element)) {
105-
return null;
106-
}
107-
return element.querySelector(selector) || null;
108-
}
109-
11056
/*
11157
* Utility Methods
11258
*/
@@ -269,10 +215,10 @@ ScrollSpy.prototype.refresh = function () {
269215
this._scrollHeight = this._getScrollHeight();
270216

271217
// Find all nav link/dropdown/list-item links in our element
272-
$QSA(this._selector, this._$el).map(el => {
218+
selectAll(this._selector, this._$el).map(el => {
273219
const href = el.getAttribute('href');
274220
if (href && href.charAt(0) === '#' && href !== '#' && href.indexOf('#/') === -1) {
275-
const target = $QS(href, scroller);
221+
const target = select(href, scroller);
276222
if (!target) {
277223
return null;
278224
}
@@ -406,7 +352,7 @@ ScrollSpy.prototype._getScroller = function () {
406352
return document.body;
407353
}
408354
// Otherwise assume CSS selector
409-
return $QS(scroller);
355+
return select(scroller);
410356
}
411357
return null;
412358
};
@@ -450,14 +396,14 @@ ScrollSpy.prototype._activate = function (target) {
450396
return selector + '[href="' + target + '"]';
451397
});
452398

453-
const links = $QSA(queries.join(','), this._$el);
399+
const links = selectAll(queries.join(','), this._$el);
454400

455401
links.forEach(link => {
456402
if (link.classList.contains(ClassName.DROPDOWN_ITEM)) {
457403
// This is a dropdown item, so find the .dropdown-toggle and set it's state
458-
const dropdown = closest(link, Selector.DROPDOWN);
404+
const dropdown = closest(Selector.DROPDOWN, link);
459405
if (dropdown) {
460-
const toggle = $QS(Selector.DROPDOWN_TOGGLE, dropdown);
406+
const toggle = select(Selector.DROPDOWN_TOGGLE, dropdown);
461407
if (toggle) {
462408
this._setActiveState(toggle, true);
463409
}
@@ -482,7 +428,7 @@ ScrollSpy.prototype._activate = function (target) {
482428

483429
// Clear the 'active' targets in our nav component
484430
ScrollSpy.prototype._clear = function () {
485-
$QSA(this._selector, this._$el).filter(el => {
431+
selectAll(this._selector, this._$el).filter(el => {
486432
if (el.classList.contains(ClassName.ACTIVE)) {
487433
const href = el.getAttribute('href');
488434
if (href.charAt(0) !== '#' || href.indexOf('#/') === 0) {
@@ -522,7 +468,7 @@ ScrollSpy.prototype._setParentsSiblingActiveState = function (element, selector,
522468
}
523469
let el = element;
524470
while (el) {
525-
el = closest(el, selector);
471+
el = closest(selector, el);
526472
if (el && el.previousElementSibling) {
527473
for (let i = 0; i < classes.length - 1; i++) {
528474
if (el.previousElementSibling.classList.contains(classes[i])) {

0 commit comments

Comments
 (0)