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

Commit

Permalink
fix(tooltip): fixes tooltip position when body has a margin
Browse files Browse the repository at this point in the history
Closes #1883
  • Loading branch information
Robert Messerle committed Mar 18, 2015
1 parent 1a53726 commit 00f4cc6
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 56 deletions.
122 changes: 66 additions & 56 deletions src/components/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
* @ngdoc module
* @name material.components.tooltip
*/
angular.module('material.components.tooltip', [
'material.core'
])
.directive('mdTooltip', MdTooltipDirective);
angular
.module('material.components.tooltip', [ 'material.core' ])
.directive('mdTooltip', MdTooltipDirective);

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

var TOOLTIP_SHOW_DELAY = 0;
var TOOLTIP_SHOW_DELAY = 300;
var TOOLTIP_WINDOW_EDGE_SPACE = 8;

return {
restrict: 'E',
transclude: true,
template:
'<div class="md-background"></div>' +
'<div class="md-content" ng-transclude></div>',
template: '\
<div class="md-background"></div>\
<div class="md-content" ng-transclude></div>',
scope: {
visible: '=?mdVisible',
delay: '=?mdDelay'
},
link: postLink
};

function postLink(scope, element, attr, contentCtrl) {
function postLink(scope, element, attr) {

$mdTheming(element);
var parent = element.parent();
var background = angular.element(element[0].getElementsByClassName('md-background')[0]);
var content = angular.element(element[0].getElementsByClassName('md-content')[0]);
var direction = attr.mdDirection;

// Keep looking for a higher parent if our current one has no pointer events
while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
parent = parent.parent();
}

// Look for the nearest parent md-content, stopping at the rootElement.
var current = element.parent()[0];
while (current && current !== $rootElement[0] && current !== document.body) {
if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
current = current.parentNode;
var parent = getParentWithPointerEvents(),
background = angular.element(element[0].getElementsByClassName('md-background')[0]),
content = angular.element(element[0].getElementsByClassName('md-content')[0]),
direction = attr.mdDirection,
current = getNearestContentElement(),
tooltipParent = angular.element(current || document.body),
debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });

return init();

function init () {
setDefaults();
manipulateElement();
bindEvents();
configureWatchers();
}
var tooltipParent = angular.element(current || document.body);

if (!angular.isDefined(attr.mdDelay)) {
scope.delay = TOOLTIP_SHOW_DELAY;
function setDefaults () {
if (!angular.isDefined(attr.mdDelay)) scope.delay = TOOLTIP_SHOW_DELAY;
}

// We will re-attach tooltip when visible
element.detach();
element.attr('role', 'tooltip');
element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));

parent.on('focus mouseenter touchstart', function() { setVisible(true); });
parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0]) setVisible(false); });
function configureWatchers () {
scope.$watch('visible', function (isVisible) {
if (isVisible) showTooltip();
else hideTooltip();
});
scope.$on('$destroy', function() {
scope.visible = false;
element.remove();
angular.element($window).off('resize', debouncedOnResize);
});
}

scope.$watch('visible', function(isVisible) {
if (isVisible) showTooltip();
else hideTooltip();
});
function manipulateElement () {
element.detach();
element.attr('role', 'tooltip');
element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
}

var debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
angular.element($window).on('resize', debouncedOnResize);
function getParentWithPointerEvents () {
var parent = element.parent();
while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
parent = parent.parent();
}
return parent;
}

// Be sure to completely cleanup the element on destroy
scope.$on('$destroy', function() {
scope.visible = false;
element.remove();
angular.element($window).off('resize', debouncedOnResize);
});
function getNearestContentElement () {
var current = element.parent()[0];
// Look for the nearest parent md-content, stopping at the rootElement.
while (current && current !== $rootElement[0] && current !== document.body) {
if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
current = current.parentNode;
}
return current;
}

// *******
// Methods
// *******
function bindEvents () {
parent.on('focus mouseenter touchstart', function() { setVisible(true); });
parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0]) setVisible(false); });
angular.element($window).on('resize', debouncedOnResize);
}

// If setting visible to true, debounce to scope.delay ms
// If setting visible to false and no timeout is active, instantly hide the tooltip.
function setVisible (value) {
setVisible.value = !!value;
if (!setVisible.queued) {
Expand All @@ -115,7 +128,6 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
scope.visible = setVisible.value;
setVisible.queued = false;
}, scope.delay);

} else {
$timeout(function() { scope.visible = false; });
}
Expand All @@ -127,12 +139,10 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
parent.attr('aria-describedby', element.attr('id'));
tooltipParent.append(element);

// Wait until the element has been in the dom for two frames before fading it in.
// Additionally, we position the tooltip twice to avoid positioning bugs
positionTooltip();
$animate.addClass(element, 'md-show');
$animate.addClass(background, 'md-show');
$animate.addClass(content, 'md-show');
angular.forEach([element, background, content], function (element) {
$animate.addClass(element, 'md-show');
});
}

function hideTooltip() {
Expand Down
1 change: 1 addition & 0 deletions src/core/style/structure.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ html, body {
height: 100%;
color: rgba(0,0,0,0.87);
background: white;
position: relative;

-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-touch-callout: none;
Expand Down

0 comments on commit 00f4cc6

Please sign in to comment.