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

Commit c8821eb

Browse files
crisbetoThomasBurleson
authored andcommitted
fix(tooltip): fix regression on touch devices with a mouse
Reverts most of the changes from #8700 and switches to a simpler approach that uses a repeated `touchstart` to hide the tooltip, instead of `touchend`, which fires too early. Also re-enables mouse events for all kinds of devices, in order to cover cases where the devices has both a touchscreen and a mouse. Fixes #8710. Closes #8730
1 parent 8b3abc1 commit c8821eb

File tree

3 files changed

+44
-35
lines changed

3 files changed

+44
-35
lines changed

src/components/tooltip/tooltip.js

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,11 @@ angular
3434
* @param {string=} md-direction Which direction would you like the tooltip to go? Supports left, right, top, and bottom. Defaults to bottom.
3535
*/
3636
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement,
37-
$animate, $q, $interpolate, $mdConstant) {
38-
39-
// Note that touch devices still fire mouse events, even though they don't have a
40-
// mouse. We shouldn't bind them in those cases, because it causes the callbacks
41-
// to fire too often.
42-
var ENTER_EVENTS = 'focus ' + ($mdConstant.IS_TOUCH ? 'touchstart' : 'mouseenter');
43-
var LEAVE_EVENTS = 'blur ' + ($mdConstant.IS_TOUCH ? 'touchend touchcancel' : 'mouseleave');
44-
var TOOLTIP_SHOW_DELAY = $mdConstant.IS_TOUCH ? 75 : 0;
37+
$animate, $q, $interpolate) {
38+
39+
var ENTER_EVENTS = 'focus touchstart mouseenter';
40+
var LEAVE_EVENTS = 'blur touchcancel mouseleave';
41+
var TOOLTIP_SHOW_DELAY = 0;
4542
var TOOLTIP_WINDOW_EDGE_SPACE = 8;
4643

4744
return {
@@ -202,9 +199,9 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
202199

203200
// Store whether the element was focused when the window loses focus.
204201
var windowBlurHandler = function() {
205-
preventNextFocus = document.activeElement === parent[0];
202+
elementFocusedOnWindowBlur = document.activeElement === parent[0];
206203
};
207-
var preventNextFocus = false;
204+
var elementFocusedOnWindowBlur = false;
208205

209206
function windowScrollHandler() {
210207
setVisible(false);
@@ -233,18 +230,19 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
233230

234231
var enterHandler = function(e) {
235232
// Prevent the tooltip from showing when the window is receiving focus.
236-
if (e.type === 'focus' && preventNextFocus) {
237-
preventNextFocus = false;
233+
if (e.type === 'focus' && elementFocusedOnWindowBlur) {
234+
elementFocusedOnWindowBlur = false;
235+
} else if (e.type === 'touchstart' && scope.visible) {
236+
leaveHandler();
238237
} else if (!scope.visible) {
239-
parent.on(LEAVE_EVENTS, leaveHandler );
238+
parent.on(LEAVE_EVENTS, leaveHandler);
240239
setVisible(true);
241240
}
242241
};
243242
var leaveHandler = function () {
244243
var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
245244

246-
if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) {
247-
245+
if (autohide || mouseActive || $document[0].activeElement !== parent[0]) {
248246
// When a show timeout is currently in progress, then we have to cancel it.
249247
// Otherwise the tooltip will remain showing without focus or hover.
250248
if (showTimeout) {
@@ -253,22 +251,19 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
253251
showTimeout = null;
254252
}
255253

256-
parent.off(LEAVE_EVENTS, leaveHandler );
254+
parent.off(LEAVE_EVENTS, leaveHandler);
257255
parent.triggerHandler('blur');
258256
setVisible(false);
259257
}
260258
mouseActive = false;
261259
};
262260
var mousedownHandler = function() {
263-
// Don't show the tooltip when tapping on a touch device,
264-
// in order to prevent the tooltip staying open after random taps.
265-
preventNextFocus = $mdConstant.IS_TOUCH;
266261
mouseActive = true;
267262
};
268263

269264
// to avoid `synthetic clicks` we listen to mousedown instead of `click`
270265
parent.on('mousedown', mousedownHandler);
271-
parent.on(ENTER_EVENTS, enterHandler );
266+
parent.on(ENTER_EVENTS, enterHandler);
272267
}
273268

274269
function setVisible (value) {

src/components/tooltip/tooltip.spec.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
describe('<md-tooltip> directive', function() {
2-
var $compile, $rootScope, $material, $timeout, leaveEvent, enterEvent;
2+
var $compile, $rootScope, $material, $timeout;
33
var element;
44

55
beforeEach(module('material.components.tooltip'));
66
beforeEach(module('material.components.button'));
7-
beforeEach(inject(function(_$compile_, _$rootScope_, _$material_, _$timeout_, $mdConstant){
7+
beforeEach(inject(function(_$compile_, _$rootScope_, _$material_, _$timeout_){
88
$compile = _$compile_;
99
$rootScope = _$rootScope_;
1010
$material = _$material_;
1111
$timeout = _$timeout_;
12-
leaveEvent = $mdConstant.IS_TOUCH ? 'touchend' : 'mouseleave';
13-
enterEvent = $mdConstant.IS_TOUCH ? 'touchstart' : 'mouseenter';
1412
}));
1513
afterEach(function() {
1614
// Make sure to remove/cleanup after each test
@@ -114,7 +112,7 @@ describe('<md-tooltip> directive', function() {
114112
'</outer>'
115113
);
116114

117-
triggerEvent(enterEvent, true);
115+
triggerEvent('mouseenter', true);
118116
expect($rootScope.testModel.isVisible).toBeUndefined();
119117

120118
}));
@@ -167,7 +165,7 @@ describe('<md-tooltip> directive', function() {
167165
expect(findTooltip().length).toBe(0);
168166
});
169167

170-
it('should set visible on enter and leave events', function() {
168+
it('should set visible on mouseenter and mouseleave', function() {
171169
buildTooltip(
172170
'<md-button>' +
173171
'Hello' +
@@ -177,14 +175,31 @@ describe('<md-tooltip> directive', function() {
177175
'</md-button>'
178176
);
179177

180-
triggerEvent(enterEvent);
178+
triggerEvent('mouseenter');
181179
expect($rootScope.testModel.isVisible).toBe(true);
182180

183-
triggerEvent(leaveEvent);
181+
triggerEvent('mouseleave');
184182
expect($rootScope.testModel.isVisible).toBe(false);
185183
});
186184

187-
it('should cancel when the leave event was before the delay', function() {
185+
it('should should toggle visibility on touch start', function() {
186+
buildTooltip(
187+
'<md-button>' +
188+
'Hello' +
189+
'<md-tooltip md-visible="testModel.isVisible">' +
190+
'Tooltip' +
191+
'</md-tooltip>' +
192+
'</md-button>'
193+
);
194+
195+
triggerEvent('touchstart');
196+
expect($rootScope.testModel.isVisible).toBe(true);
197+
198+
triggerEvent('touchstart');
199+
expect($rootScope.testModel.isVisible).toBe(false);
200+
});
201+
202+
it('should cancel when mouseleave was before the delay', function() {
188203
buildTooltip(
189204
'<md-button>' +
190205
'Hello' +
@@ -195,10 +210,10 @@ describe('<md-tooltip> directive', function() {
195210
);
196211

197212

198-
triggerEvent(enterEvent, true);
213+
triggerEvent('mouseenter', true);
199214
expect($rootScope.testModel.isVisible).toBeFalsy();
200215

201-
triggerEvent(leaveEvent, true);
216+
triggerEvent('mouseleave', true);
202217
expect($rootScope.testModel.isVisible).toBeFalsy();
203218

204219
// Total 99 == tooltipDelay
@@ -251,7 +266,7 @@ describe('<md-tooltip> directive', function() {
251266
expect($rootScope.testModel.isVisible).toBe(false);
252267
});
253268

254-
it('should not be visible when a leave event fires right after a mousedown', inject(function($document) {
269+
it('should not be visible on mousedown and then mouseleave', inject(function($document) {
255270
buildTooltip(
256271
'<md-button>' +
257272
'Hello' +
@@ -269,7 +284,7 @@ describe('<md-tooltip> directive', function() {
269284
expect($document[0].activeElement).toBe(element[0]);
270285
expect($rootScope.testModel.isVisible).toBe(true);
271286

272-
triggerEvent(leaveEvent);
287+
triggerEvent('mouseleave');
273288
expect($rootScope.testModel.isVisible).toBe(false);
274289

275290
// Clean up document.body.
@@ -292,7 +307,7 @@ describe('<md-tooltip> directive', function() {
292307
triggerEvent('focus,mousedown');
293308
expect(document.activeElement).toBe(element[0]);
294309

295-
triggerEvent(leaveEvent);
310+
triggerEvent('mouseleave');
296311

297312
// Simulate tabbing away.
298313
angular.element($window).triggerHandler('blur');

src/core/util/constant.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ function MdConstantFactory($sniffer, $window, $document) {
3434
}
3535

3636
return {
37-
IS_TOUCH: ('ontouchstart' in $window) || $window.DocumentTouch && $document[0] instanceof DocumentTouch,
3837
KEY_CODE: {
3938
COMMA: 188,
4039
SEMICOLON : 186,

0 commit comments

Comments
 (0)