diff --git a/src/tooltip/test/tooltip2.spec.js b/src/tooltip/test/tooltip2.spec.js
index bb7e6ebba5..afdccef7c3 100644
--- a/src/tooltip/test/tooltip2.spec.js
+++ b/src/tooltip/test/tooltip2.spec.js
@@ -1,14 +1,15 @@
describe('tooltip directive', function () {
- var $rootScope, $compile, $document, $timeout;
+ var $rootScope, $compile, $document, $timeout, $position;
beforeEach(module('ui.bootstrap.tooltip'));
beforeEach(module('template/tooltip/tooltip-popup.html'));
- beforeEach(inject(function (_$rootScope_, _$compile_, _$document_, _$timeout_) {
+ beforeEach(inject(function (_$rootScope_, _$compile_, _$document_, _$timeout_, _$position_) {
$rootScope = _$rootScope_;
$compile = _$compile_;
$document = _$document_;
$timeout = _$timeout_;
+ $position = _$position_;
}));
beforeEach(function(){
@@ -102,4 +103,38 @@ describe('tooltip directive', function () {
});
});
+
+ describe('position', function() {
+
+ beforeEach(function() {
+ spyOn($position, 'position').andCallThrough();
+ });
+
+ it('is called once on mouse enter and leave', function () {
+ var fragment = compileTooltip('Trigger here');
+
+ expect($position.position).not.toHaveBeenCalled();
+
+ fragment.find('span').trigger( 'mouseenter' );
+ expect($position.position.calls.length).toEqual(1);
+
+ closeTooltip(fragment.find('span'));
+ expect($position.position.calls.length).toEqual(1);
+ });
+
+ it('is called once for content updates when is open', function () {
+ $rootScope.content = 'some text';
+ var fragment = compileTooltip('Trigger here');
+
+ expect($position.position).not.toHaveBeenCalled();
+
+ fragment.find('span').trigger( 'mouseenter' );
+ expect($position.position.calls.length).toEqual(1);
+
+ $rootScope.content = 'some new text';
+ $rootScope.$digest();
+ $timeout.flush();
+ expect($position.position.calls.length).toEqual(1);
+ });
+ });
});
\ No newline at end of file
diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js
index 3176f96902..46e17f2f6f 100644
--- a/src/tooltip/tooltip.js
+++ b/src/tooltip/tooltip.js
@@ -109,13 +109,15 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
restrict: 'EA',
scope: true,
link: function link ( scope, element, attrs ) {
- var tooltip = $compile( template )( scope );
- var transitionTimeout;
- var popupTimeout;
- var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
- var triggers = getTriggers( undefined );
- var hasRegisteredTriggers = false;
- var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
+ var tooltip = $compile( template )( scope ),
+ transitionTimeout,
+ popupTimeout,
+ appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false,
+ triggers = getTriggers( undefined ),
+ hasRegisteredTriggers = false,
+ hasEnableExp = angular.isDefined(attrs[prefix+'Enable']),
+ position,
+ positionUpdateTimeout;
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
@@ -147,77 +149,74 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
});
}
- // Show the tooltip popup element.
- function show() {
- var position,
- ttWidth,
- ttHeight,
+ // Calculate tootip's position
+ function updatePosition() {
+ var ttWidth = tooltip.prop( 'offsetWidth' ),
+ ttHeight = tooltip.prop( 'offsetHeight' ),
ttPosition;
-
- // Don't show empty tooltips.
- if ( ! scope.tt_content ) {
- return;
- }
-
- // If there is a pending remove transition, we must cancel it, lest the
- // tooltip be mysteriously removed.
- if ( transitionTimeout ) {
- $timeout.cancel( transitionTimeout );
- }
-
- // Set the initial positioning.
- tooltip.css({ top: 0, left: 0, display: 'block' });
-
- // Now we add it to the DOM because need some info about it. But it's not
- // visible yet anyway.
- if ( appendToBody ) {
- $document.find( 'body' ).append( tooltip );
- } else {
- element.after( tooltip );
- }
-
- // Get the position of the directive element.
- position = appendToBody ? $position.offset( element ) : $position.position( element );
-
- // Get the height and width of the tooltip so we can center it.
- ttWidth = tooltip.prop( 'offsetWidth' );
- ttHeight = tooltip.prop( 'offsetHeight' );
// Calculate the tooltip's top and left coordinates to center it with
// this directive.
switch ( scope.tt_placement ) {
case 'right':
ttPosition = {
- top: position.top + position.height / 2 - ttHeight / 2,
- left: position.left + position.width
+ top: position.top + position.height / 2 - ttHeight / 2 + 'px',
+ left: position.left + position.width + 'px'
};
break;
case 'bottom':
ttPosition = {
- top: position.top + position.height,
- left: position.left + position.width / 2 - ttWidth / 2
+ top: position.top + position.height + 'px',
+ left: position.left + position.width / 2 - ttWidth / 2 + 'px'
};
break;
case 'left':
ttPosition = {
- top: position.top + position.height / 2 - ttHeight / 2,
- left: position.left - ttWidth
+ top: position.top + position.height / 2 - ttHeight / 2 + 'px',
+ left: position.left - ttWidth + 'px'
};
break;
default:
ttPosition = {
- top: position.top - ttHeight,
- left: position.left + position.width / 2 - ttWidth / 2
+ top: position.top - ttHeight + 'px',
+ left: position.left + position.width / 2 - ttWidth / 2 + 'px'
};
break;
}
- ttPosition.top += 'px';
- ttPosition.left += 'px';
-
// Now set the calculated positioning.
tooltip.css( ttPosition );
-
+ }
+
+ // Show the tooltip popup element.
+ function show() {
+ // Don't show empty tooltips.
+ if ( ! scope.tt_content ) {
+ return;
+ }
+
+ // If there is a pending remove transition, we must cancel it, lest the
+ // tooltip be mysteriously removed.
+ if ( transitionTimeout ) {
+ $timeout.cancel( transitionTimeout );
+ }
+
+ tooltip.css({ display: 'block' });
+
+ // Now we add it to the DOM because need some info about it. But it's not
+ // visible yet anyway.
+ if ( appendToBody ) {
+ $document.find( 'body' ).append( tooltip );
+ } else {
+ element.after( tooltip );
+ }
+
+ // Get the position of the directive element.
+ position = appendToBody ? $position.offset( element ) : $position.position( element );
+
+ // Place tooltip
+ updatePosition();
+
// And show the tooltip.
scope.tt_isOpen = true;
}
@@ -248,8 +247,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
attrs.$observe( type, function ( val ) {
scope.tt_content = val;
- if (!val && scope.tt_isOpen ) {
- hide();
+ if (scope.tt_isOpen) {
+ if (!val) {
+ hide();
+ } else {
+ // Wait for content to be set on the tooltip element and adjust position
+ // based on the new width/height and last known position of the directive element.
+ positionUpdateTimeout = $timeout(updatePosition, 0, false);
+ }
}
});
@@ -311,6 +316,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
scope.$on('$destroy', function onDestroyTooltip() {
$timeout.cancel( transitionTimeout );
$timeout.cancel( popupTimeout );
+ $timeout.cancel( positionUpdateTimeout );
unregisterTriggers();
tooltip.remove();
tooltip.unbind();