Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 5ba4c0e

Browse files
devversionThomasBurleson
authored andcommitted
fix(tooltip): cancel show timeouts when leaving before delay.
* When a user hovers over a tooltip, which has a delay, and we're leaving the tooltips parent before the delay is finished, then we're detaching all leave handlers, which is not valid. We should cancel the show timeout, when the leave occurs before the delay. Fixes #8363. Closes #8394
1 parent 6c549f5 commit 5ba4c0e

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

src/components/tooltip/tooltip.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
5959
var parent = $mdUtil.getParentWithPointerEvents(element),
6060
content = angular.element(element[0].getElementsByClassName('_md-content')[0]),
6161
tooltipParent = angular.element(document.body),
62+
showTimeout = null,
6263
debouncedOnResize = $$rAF.throttle(function () { updatePosition(); });
6364

6465
if ($animate.pin) $animate.pin(element, parent);
@@ -194,7 +195,17 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
194195
};
195196
var leaveHandler = function () {
196197
var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
198+
197199
if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) {
200+
201+
// When a show timeout is currently in progress, then we have to cancel it.
202+
// Otherwise the tooltip will remain showing without focus or hover.
203+
if (showTimeout) {
204+
$timeout.cancel(showTimeout);
205+
setVisible.queued = false;
206+
showTimeout = null;
207+
}
208+
198209
parent.off('blur mouseleave touchend touchcancel', leaveHandler );
199210
parent.triggerHandler("blur");
200211
setVisible(false);
@@ -214,14 +225,18 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
214225
if (setVisible.queued && setVisible.visible === !!value || scope.visible === !!value) return;
215226

216227
setVisible.value = !!value;
228+
217229
if (!setVisible.queued) {
218230
if (value) {
219231
setVisible.queued = true;
220-
$timeout(function() {
232+
showTimeout = $timeout(function() {
221233
scope.visible = setVisible.value;
222234
setVisible.queued = false;
223-
if (!scope.visibleWatcher)
235+
showTimeout = null;
236+
237+
if (!scope.visibleWatcher) {
224238
onVisibleChanged(scope.visible);
239+
}
225240
}, scope.delay);
226241
} else {
227242
$mdUtil.nextTick(function() {

src/components/tooltip/tooltip.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,29 @@ describe('<md-tooltip> directive', function() {
141141
expect($rootScope.testModel.isVisible).toBe(false);
142142
});
143143

144+
it('should cancel when mouseleave was before the delay', function() {
145+
buildTooltip(
146+
'<md-button>' +
147+
'Hello' +
148+
'<md-tooltip md-delay="99" md-autohide md-visible="testModel.isVisible">' +
149+
'Tooltip' +
150+
'</md-tooltip>' +
151+
'</md-button>'
152+
);
153+
154+
155+
triggerEvent('mouseenter', true);
156+
expect($rootScope.testModel.isVisible).toBeFalsy();
157+
158+
triggerEvent('mouseleave', true);
159+
expect($rootScope.testModel.isVisible).toBeFalsy();
160+
161+
// Total 99 == tooltipDelay
162+
$timeout.flush(99);
163+
164+
expect($rootScope.testModel.isVisible).toBe(false);
165+
});
166+
144167
it('should set visible on focus and blur', function() {
145168
buildTooltip(
146169
'<md-button>' +

0 commit comments

Comments
 (0)