Skip to content

Commit 6b12629

Browse files
authored
feat(popover+tooltip): Add hide event listener on $root (bootstrap-vue#1003)
* feat(popover/tooltip): Add hide on $root events Optimized adding/removing of events. Only listen/watch to certain events while the tooltip/popover is open Adds listener for `bv::hide::(popover|tooltip)` event on root to allow user to close all popovers or tooltips via emitting on $root. * [popover component] Document bv:hide::popover event * [tooltip component] Document bv:hide:tooltip event * [popover directive] Document bv:hide:popover event * [tooltip directive]: Document bv:hide::tooltip event
1 parent 36acf4e commit 6b12629

File tree

5 files changed

+68
-33
lines changed

5 files changed

+68
-33
lines changed

docs/components/popover/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,13 @@ small screens can be harder to deal with on mobile devices (such as smart-phones
284284
<!-- popover-advanced-1.vue -->
285285
```
286286

287+
## Closing popovers
288+
You can close all open popovers by emitting the `bv::hide::popover` event on $root:
289+
290+
```js
291+
this.$root.$emit('bv::hide::popover');
292+
```
293+
287294
## Accessibility
288295
Popovers, in their current state, are not overly accessible when used as interactive
289296
components. Content may not be activly read to screen reader users, and the popover

docs/components/tooltip/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,11 @@ The `v-b-tooltip` directive makes adding tooltips even easier, without additiona
108108

109109
Refer to the [`v-b-tooltip` documentation](/docs/directives/tooltip) for more information
110110
and features of the directive format.
111+
112+
## Closing tooltips
113+
You can close all open tooltips by emitting the `bv::hide::tooltip` event on $root:
114+
115+
```js
116+
this.$root.$emit('bv::hide::tooltip');
117+
```
118+

docs/directives/popover/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,10 @@ a focus change via pressing the <kbd>TAB</kbd> key). Some call this behavior _se
206206

207207
<!-- popover-2.vue -->
208208
```
209+
210+
## Closing popovers
211+
You can close all open popovers by emitting the `bv::hide::popover` event on $root:
212+
213+
```js
214+
this.$root.$emit('bv::hide::popover');
215+
```

docs/directives/tooltip/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,10 @@ receives focus.
143143
Note that your elment **must** be in the document tab sequence for this to work. If
144144
your element is not tabable, add the `tabindex="0"` attribute to the element.
145145

146+
147+
## Closing tooltips
148+
You can close all open tooltips by emitting the `bv::hide::tooltip` event on $root:
149+
150+
```js
151+
this.$root.$emit('bv::hide::tooltip');
152+
```

lib/classes/tooltip.js

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -178,21 +178,26 @@ class ToolTip {
178178

179179
// Destroy this instance
180180
destroy() {
181-
this.visibleCheck(false);
181+
// Stop listening to trigger events
182+
this.unListen();
183+
// Disable while open listeners/watchers
184+
this.setWhileOpenListeners(false);
185+
// Clear any timouts
182186
clearTimeout(this.$hoverTimeout);
183187
this.$hoverTimeout = null;
184188
clearTimeout(this.$fadeTimeout);
185189
this.$fadeTimeout = null;
186-
this.unListen();
187-
this.setOnTouchStartListener(false);
190+
// Remove popper
188191
if (this.$popper) {
189192
this.$popper.destroy();
190193
}
191194
this.$popper = null;
195+
// Remove tip from document
192196
if (this.$tip && this.$tip.parentElement) {
193197
this.$tip.parentElement.removeChild(this.$tip);
194198
}
195199
this.$tip = null;
200+
// Null out other properties
196201
this.$id = null
197202
this.$root = null;
198203
this.$element = null;
@@ -289,13 +294,11 @@ class ToolTip {
289294
this.emitEvent(shownEvt);
290295
};
291296

297+
// Enable while open listeners/watchers
298+
this.setWhileOpenListeners(true);
299+
292300
// Show tip
293301
tip.classList.add(ClassName.SHOW);
294-
this.setOnTouchStartListener(true);
295-
296-
// Periodically check to make sure $element is visible
297-
// For handling when tip is in <keepalive>, tabs, carousel, etc
298-
this.visibleCheck(true);
299302

300303
// Start the transition/animation
301304
this.transitionOnce(tip, complete);
@@ -316,6 +319,20 @@ class ToolTip {
316319
}
317320
}
318321

322+
setWhileOpenListeners(on) {
323+
// Ontouch start listeners
324+
this.setOnTouchStartListener(on);
325+
// Global hide events
326+
this.setRootListener(on);
327+
// Modal close events
328+
this.setModalListener(on);
329+
// Route change events
330+
this.setRouteWatcher(on);
331+
// Periodic $element visibility check
332+
// For handling when tip is in <keepalive>, tabs, carousel, etc
333+
this.visibleCheck(on);
334+
}
335+
319336
// force hide of tip (internal method)
320337
forceHide() {
321338
const tip = this.getTipElement();
@@ -349,9 +366,6 @@ class ToolTip {
349366
return;
350367
}
351368

352-
// Stop checking for visibility of element.
353-
this.visibleCheck(false);
354-
355369
// Transitionend Callback
356370
const complete = () => {
357371
if (this.$hoverState !== HoverState.SHOW && tip.parentNode) {
@@ -374,8 +388,10 @@ class ToolTip {
374388
this.emitEvent(hiddenEvt);
375389
};
376390

391+
// Disable while open listeners/watchers
392+
this.setWhileOpenListeners(false);
393+
377394
// Hide tip
378-
this.setOnTouchStartListener(false);
379395
tip.classList.remove(ClassName.SHOW);
380396

381397
this.$activeTrigger.click = false;
@@ -582,10 +598,6 @@ class ToolTip {
582598
this.$element.addEventListener('mouseleave', this);
583599
}
584600
}, this);
585-
// If we are in a modal, we need to hide when it closes
586-
this.setModalListener(true);
587-
// Watch for route changes
588-
this.setRouteWatcher(true);
589601
}
590602

591603
unListen() {
@@ -594,9 +606,6 @@ class ToolTip {
594606
events.forEach(evt => {
595607
this.$element.removeEventListener(evt, this);
596608
}, this);
597-
this.setModalListener(false);
598-
// stop watching for route changes
599-
this.setRouteWatcher(false);
600609
}
601610

602611
handleEvent(e) {
@@ -648,15 +657,16 @@ class ToolTip {
648657
}
649658
// We can listen for modal hidden events on $root
650659
if (this.$root) {
651-
if (on) {
652-
MODAL_CLOSE_EVENT.forEach(evtName => {
653-
this.$root.$on(evtName, this.forceHide.bind(this));
654-
});
655-
} else {
656-
MODAL_CLOSE_EVENT.forEach(evtName => {
657-
this.$root.$off(evtName, this.forceHide.bind(this));
658-
});
659-
}
660+
MODAL_CLOSE_EVENT.forEach(evtName => {
661+
this.$root[on ? '$on' : '$off'](evtName, this.forceHide.bind(this));
662+
});
663+
}
664+
}
665+
666+
setRootListener(on) {
667+
// We can listen for global 'bv::hide::popover/tooltip' hide request event
668+
if (this.$root) {
669+
this.$root[on ? '$on' : '$off'](`bv::hide::${this.constructor.NAME}`, this.forceHide.bind(this));
660670
}
661671
}
662672

@@ -667,11 +677,7 @@ class ToolTip {
667677
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
668678
if ('ontouchstart' in document.documentElement) {
669679
arrayFrom(document.body.children).forEach(el => {
670-
if (on) {
671-
el.addEventListener('mouseover', this.noop);
672-
} else {
673-
el.removeEventListener('mouseover', this.noop);
674-
}
680+
el[on ? 'addEventListener' : 'removeEventListener']('mouseover', this.noop);
675681
});
676682
}
677683
}

0 commit comments

Comments
 (0)