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

Commit 00f4cc6

Browse files
author
Robert Messerle
committed
fix(tooltip): fixes tooltip position when body has a margin
Closes #1883
1 parent 1a53726 commit 00f4cc6

2 files changed

Lines changed: 67 additions & 56 deletions

File tree

src/components/tooltip/tooltip.js

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
* @ngdoc module
66
* @name material.components.tooltip
77
*/
8-
angular.module('material.components.tooltip', [
9-
'material.core'
10-
])
11-
.directive('mdTooltip', MdTooltipDirective);
8+
angular
9+
.module('material.components.tooltip', [ 'material.core' ])
10+
.directive('mdTooltip', MdTooltipDirective);
1211

1312
/**
1413
* @ngdoc directive
@@ -37,75 +36,89 @@ angular.module('material.components.tooltip', [
3736
*/
3837
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement, $animate, $q) {
3938

40-
var TOOLTIP_SHOW_DELAY = 0;
39+
var TOOLTIP_SHOW_DELAY = 300;
4140
var TOOLTIP_WINDOW_EDGE_SPACE = 8;
4241

4342
return {
4443
restrict: 'E',
4544
transclude: true,
46-
template:
47-
'<div class="md-background"></div>' +
48-
'<div class="md-content" ng-transclude></div>',
45+
template: '\
46+
<div class="md-background"></div>\
47+
<div class="md-content" ng-transclude></div>',
4948
scope: {
5049
visible: '=?mdVisible',
5150
delay: '=?mdDelay'
5251
},
5352
link: postLink
5453
};
5554

56-
function postLink(scope, element, attr, contentCtrl) {
55+
function postLink(scope, element, attr) {
56+
5757
$mdTheming(element);
58-
var parent = element.parent();
59-
var background = angular.element(element[0].getElementsByClassName('md-background')[0]);
60-
var content = angular.element(element[0].getElementsByClassName('md-content')[0]);
61-
var direction = attr.mdDirection;
62-
63-
// Keep looking for a higher parent if our current one has no pointer events
64-
while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
65-
parent = parent.parent();
66-
}
6758

68-
// Look for the nearest parent md-content, stopping at the rootElement.
69-
var current = element.parent()[0];
70-
while (current && current !== $rootElement[0] && current !== document.body) {
71-
if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
72-
current = current.parentNode;
59+
var parent = getParentWithPointerEvents(),
60+
background = angular.element(element[0].getElementsByClassName('md-background')[0]),
61+
content = angular.element(element[0].getElementsByClassName('md-content')[0]),
62+
direction = attr.mdDirection,
63+
current = getNearestContentElement(),
64+
tooltipParent = angular.element(current || document.body),
65+
debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
66+
67+
return init();
68+
69+
function init () {
70+
setDefaults();
71+
manipulateElement();
72+
bindEvents();
73+
configureWatchers();
7374
}
74-
var tooltipParent = angular.element(current || document.body);
7575

76-
if (!angular.isDefined(attr.mdDelay)) {
77-
scope.delay = TOOLTIP_SHOW_DELAY;
76+
function setDefaults () {
77+
if (!angular.isDefined(attr.mdDelay)) scope.delay = TOOLTIP_SHOW_DELAY;
7878
}
7979

80-
// We will re-attach tooltip when visible
81-
element.detach();
82-
element.attr('role', 'tooltip');
83-
element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
84-
85-
parent.on('focus mouseenter touchstart', function() { setVisible(true); });
86-
parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0]) setVisible(false); });
80+
function configureWatchers () {
81+
scope.$watch('visible', function (isVisible) {
82+
if (isVisible) showTooltip();
83+
else hideTooltip();
84+
});
85+
scope.$on('$destroy', function() {
86+
scope.visible = false;
87+
element.remove();
88+
angular.element($window).off('resize', debouncedOnResize);
89+
});
90+
}
8791

88-
scope.$watch('visible', function(isVisible) {
89-
if (isVisible) showTooltip();
90-
else hideTooltip();
91-
});
92+
function manipulateElement () {
93+
element.detach();
94+
element.attr('role', 'tooltip');
95+
element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
96+
}
9297

93-
var debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
94-
angular.element($window).on('resize', debouncedOnResize);
98+
function getParentWithPointerEvents () {
99+
var parent = element.parent();
100+
while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
101+
parent = parent.parent();
102+
}
103+
return parent;
104+
}
95105

96-
// Be sure to completely cleanup the element on destroy
97-
scope.$on('$destroy', function() {
98-
scope.visible = false;
99-
element.remove();
100-
angular.element($window).off('resize', debouncedOnResize);
101-
});
106+
function getNearestContentElement () {
107+
var current = element.parent()[0];
108+
// Look for the nearest parent md-content, stopping at the rootElement.
109+
while (current && current !== $rootElement[0] && current !== document.body) {
110+
if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
111+
current = current.parentNode;
112+
}
113+
return current;
114+
}
102115

103-
// *******
104-
// Methods
105-
// *******
116+
function bindEvents () {
117+
parent.on('focus mouseenter touchstart', function() { setVisible(true); });
118+
parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0]) setVisible(false); });
119+
angular.element($window).on('resize', debouncedOnResize);
120+
}
106121

107-
// If setting visible to true, debounce to scope.delay ms
108-
// If setting visible to false and no timeout is active, instantly hide the tooltip.
109122
function setVisible (value) {
110123
setVisible.value = !!value;
111124
if (!setVisible.queued) {
@@ -115,7 +128,6 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
115128
scope.visible = setVisible.value;
116129
setVisible.queued = false;
117130
}, scope.delay);
118-
119131
} else {
120132
$timeout(function() { scope.visible = false; });
121133
}
@@ -127,12 +139,10 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
127139
parent.attr('aria-describedby', element.attr('id'));
128140
tooltipParent.append(element);
129141

130-
// Wait until the element has been in the dom for two frames before fading it in.
131-
// Additionally, we position the tooltip twice to avoid positioning bugs
132142
positionTooltip();
133-
$animate.addClass(element, 'md-show');
134-
$animate.addClass(background, 'md-show');
135-
$animate.addClass(content, 'md-show');
143+
angular.forEach([element, background, content], function (element) {
144+
$animate.addClass(element, 'md-show');
145+
});
136146
}
137147

138148
function hideTooltip() {

src/core/style/structure.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ html, body {
3030
height: 100%;
3131
color: rgba(0,0,0,0.87);
3232
background: white;
33+
position: relative;
3334

3435
-webkit-tap-highlight-color: rgba(0,0,0,0);
3536
-webkit-touch-callout: none;

0 commit comments

Comments
 (0)