Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
fix(tooltip): fix bugs in Safari & Firefox, as well as scrolling bugs
Browse files Browse the repository at this point in the history
Closes #593. Closes #563. Closes #467. Closes #971.
  • Loading branch information
ajoslin authored and ThomasBurleson committed Dec 19, 2014
1 parent de0198f commit 7819a39
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 15 deletions.
29 changes: 14 additions & 15 deletions src/components/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,14 @@ angular.module('material.components.tooltip', [
* @param {expression=} md-visible Boolean bound to whether the tooltip is
* currently visible.
*/
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming) {
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement) {

var TOOLTIP_SHOW_DELAY = 400;
var TOOLTIP_WINDOW_EDGE_SPACE = 8;
// We have to append tooltips to the body, because we use
// getBoundingClientRect() to find where to append the tooltip.
var tooltipParent = angular.element(document.body);

return {
restrict: 'E',
transclude: true,
require: '^?mdContent',
template:
'<div class="md-background"></div>' +
'<div class="md-content" ng-transclude></div>',
Expand All @@ -58,6 +54,14 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
$mdTheming(element);
var parent = element.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 tooltipParent = angular.element(current || document.body);

// We will re-attach tooltip when visible
element.detach();
element.attr('role', 'tooltip');
Expand Down Expand Up @@ -123,7 +127,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
// 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();
positionTooltip();
$$rAF(function() {

$$rAF(function() {
Expand All @@ -145,13 +149,8 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
}

function positionTooltip() {
var tipRect = element[0].getBoundingClientRect();
var parentRect = parent[0].getBoundingClientRect();

if (contentCtrl) {
parentRect.top += contentCtrl.$element.prop('scrollTop');
parentRect.left += contentCtrl.$element.prop('scrollLeft');
}
var tipRect = $mdUtil.elementRect(element, tooltipParent);
var parentRect = $mdUtil.elementRect(parent, tooltipParent);

// Default to bottom position if possible
var tipDirection = 'bottom';
Expand All @@ -163,12 +162,12 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
// If element bleeds over left/right of the window, place it on the edge of the window.
newPosition.left = Math.min(
newPosition.left,
$window.innerWidth - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE
tooltipParent.prop('scrollWidth') - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE
);
newPosition.left = Math.max(newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE);

// If element bleeds over the bottom of the window, place it above the parent.
if (newPosition.top + tipRect.height > $window.innerHeight) {
if (newPosition.top + tipRect.height > tooltipParent.prop('scrollHeight')) {
newPosition.top = parentRect.top - tipRect.height;
tipDirection = 'top';
}
Expand Down
22 changes: 22 additions & 0 deletions src/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ angular.module('material.core')

attachDragBehavior: attachDragBehavior,

// Firefox doesn't let us set values on the object returned from getBoundingClientRect(),
// but we need that. So we just return a new object which uses the values returned
// from getBoundingClientRect()
elementRect: function(element, offsetParent) {
var node = element[0];
offsetParent = offsetParent || node.offsetParent || document.body;
offsetParent = offsetParent[0] || offsetParent;
var rect = {
left: 0,
top: 0,
width: node.offsetWidth,
height: node.offsetHeight,
};
var current = node;
while (current && current !== offsetParent) {
rect.left += current.offsetLeft;
rect.top += current.offsetTop;
current = current.parentNode;
}
return rect;
},

/**
* Publish the iterator facade to easily support iteration and accessors
* @see iterator below
Expand Down

0 comments on commit 7819a39

Please sign in to comment.