diff --git a/src/placementcalculator.js b/src/placementcalculator.js index a4329abd..48424234 100644 --- a/src/placementcalculator.js +++ b/src/placementcalculator.js @@ -38,52 +38,52 @@ function PlacementCalculator() { // calculate the appropriate x and y position in the document switch (placement) { case 'n': - coords.set('left', position.left - (tipWidth / 2)); - coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('left', position.left - (tipWidth / 2) - session.positionCompensation.left); + coords.set('top', position.top - tipHeight - offset - session.positionCompensation.top); break; case 'e': - coords.set('left', position.left + offset); - coords.set('top', position.top - (tipHeight / 2)); + coords.set('left', position.left + offset - session.positionCompensation.left); + coords.set('top', position.top - (tipHeight / 2) - session.positionCompensation.top); break; case 's': - coords.set('left', position.left - (tipWidth / 2)); - coords.set('top', position.top + offset); + coords.set('left', position.left - (tipWidth / 2) - session.positionCompensation.left); + coords.set('top', position.top + offset - session.positionCompensation.top); break; case 'w': - coords.set('top', position.top - (tipHeight / 2)); - coords.set('right', session.windowWidth - position.left + offset); + coords.set('top', position.top - (tipHeight / 2) - session.positionCompensation.top); + coords.set('right', session.windowWidth - position.left + offset - session.positionCompensation.right); break; case 'nw': - coords.set('bottom', session.windowHeight - position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); + coords.set('top', position.top - tipHeight - offset - session.positionCompensation.top); + coords.set('right', session.windowWidth - position.left - session.positionCompensation.right - 20); break; case 'nw-alt': - coords.set('left', position.left); - coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('left', position.left - session.positionCompensation.left); + coords.set('top', position.top - tipHeight - offset - session.positionCompensation.top); break; case 'ne': - coords.set('left', position.left - 20); - coords.set('bottom', session.windowHeight - position.top + offset); + coords.set('left', position.left - session.positionCompensation.left - 20); + coords.set('top', position.top - tipHeight - offset - session.positionCompensation.top); break; case 'ne-alt': - coords.set('bottom', session.windowHeight - position.top + offset); - coords.set('right', session.windowWidth - position.left); + coords.set('top', position.top - tipHeight - offset - session.positionCompensation.top); + coords.set('right', session.windowWidth - position.left - session.positionCompensation.right); break; case 'sw': - coords.set('top', position.top + offset); - coords.set('right', session.windowWidth - position.left - 20); + coords.set('top', position.top + offset - session.positionCompensation.top); + coords.set('right', session.windowWidth - position.left - session.positionCompensation.right - 20); break; case 'sw-alt': - coords.set('left', position.left); - coords.set('top', position.top + offset); + coords.set('left', position.left - session.positionCompensation.left); + coords.set('top', position.top + offset - session.positionCompensation.top); break; case 'se': - coords.set('left', position.left - 20); - coords.set('top', position.top + offset); + coords.set('left', position.left - session.positionCompensation.left - 20); + coords.set('top', position.top + offset - session.positionCompensation.top); break; case 'se-alt': - coords.set('top', position.top + offset); - coords.set('right', session.windowWidth - position.left); + coords.set('top', position.top + offset - session.positionCompensation.top); + coords.set('right', session.windowWidth - position.left - session.positionCompensation.right); break; } diff --git a/src/tooltipcontroller.js b/src/tooltipcontroller.js index 42cdc51e..3a0f4d63 100644 --- a/src/tooltipcontroller.js +++ b/src/tooltipcontroller.js @@ -202,8 +202,8 @@ function TooltipController(options) { // support mouse-follow and fixed position tips at the same time by // moving the tooltip to the last cursor location after it is hidden - coords.set('top', session.currentY + options.offset); - coords.set('left', session.currentX + options.offset); + coords.set('top', session.currentY + options.offset - session.positionCompensation.top); + coords.set('left', session.currentX + options.offset - session.positionCompensation.left); tipElement.css(coords); // trigger powerTipClose event @@ -231,8 +231,8 @@ function TooltipController(options) { collisionCount; // grab collisions - coords.set('top', session.currentY + options.offset); - coords.set('left', session.currentX + options.offset); + coords.set('top', session.currentY + options.offset - session.positionCompensation.top); + coords.set('left', session.currentX + options.offset - session.positionCompensation.left); collisions = getViewportCollisions( coords, tipWidth, @@ -246,16 +246,16 @@ function TooltipController(options) { // if there is only one collision (bottom or right) then // simply constrain the tooltip to the view port if (collisions === Collision.right) { - coords.set('left', session.windowWidth - tipWidth); + coords.set('left', session.windowWidth - tipWidth - session.positionCompensation.left); } else if (collisions === Collision.bottom) { - coords.set('top', session.scrollTop + session.windowHeight - tipHeight); + coords.set('top', session.scrollTop + session.windowHeight - tipHeight - session.positionCompensation.top); } } else { // if the tooltip has more than one collision then it is // trapped in the corner and should be flipped to get it out // of the users way - coords.set('left', session.currentX - tipWidth - options.offset); - coords.set('top', session.currentY - tipHeight - options.offset); + coords.set('left', session.currentX - tipWidth - options.offset - session.positionCompensation.left); + coords.set('top', session.currentY - tipHeight - options.offset - session.positionCompensation.top); } } diff --git a/src/utility.js b/src/utility.js index c0d92bc0..5cfa1a95 100644 --- a/src/utility.js +++ b/src/utility.js @@ -59,6 +59,7 @@ function getViewportDimensions() { session.scrollTop = $window.scrollTop(); session.windowWidth = $window.width(); session.windowHeight = $window.height(); + session.positionCompensation = computePositionCompensation(session.windowWidth, session.windowHeight); } /** @@ -68,6 +69,7 @@ function getViewportDimensions() { function trackResize() { session.windowWidth = $window.width(); session.windowHeight = $window.height(); + session.positionCompensation = computePositionCompensation(session.windowWidth, session.windowHeight); } /** @@ -165,8 +167,10 @@ function getTooltipContent(element) { * @return {number} Value with the collision flags. */ function getViewportCollisions(coords, elementWidth, elementHeight) { - var viewportTop = session.scrollTop, - viewportLeft = session.scrollLeft, + // adjusting viewport even though it might be negative because coords + // comparing with are relative to compensated position + var viewportTop = session.scrollTop - session.positionCompensation.top, + viewportLeft = session.scrollLeft - session.positionCompensation.left, viewportBottom = viewportTop + session.windowHeight, viewportRight = viewportLeft + session.windowWidth, collisions = Collision.none; @@ -200,3 +204,41 @@ function countFlags(value) { } return count; } + +/** + * Compute compensating position offsets if body element has non-standard position attribute. + * @private + * @param {number} windowWidth Window width in pixels. + * @param {number} windowHeight Window height in pixels. + * @return {Offsets} The top, left, right, bottom offset in pixels + */ +function computePositionCompensation(windowWidth, windowHeight) { + var bodyWidthWithMargin, + bodyHeightWithMargin, + offsets, + bodyPositionPx; + + switch ($body.css('position')) { + case 'absolute': + case 'fixed': + case 'relative': + // jquery offset and position functions return top and left + // offset function computes position + margin + offsets = $body.offset(); + bodyPositionPx = $body.position(); + // because element might be positioned compute right margin using the different between + // outerWidth computations and add position offset + bodyWidthWithMargin = $body.outerWidth(true); + bodyHeightWithMargin = $body.outerHeight(true); + // right offset = right margin + body right position + offsets.right = (bodyWidthWithMargin - $body.outerWidth() - (offsets.left - bodyPositionPx.left)) + (windowWidth - bodyWidthWithMargin - bodyPositionPx.left); + // bottom offset = bottom margin + body bottom position + offsets.bottom = (bodyHeightWithMargin - $body.outerHeight() - offsets.top) + (windowHeight - bodyHeightWithMargin - bodyPositionPx.top); + break; + default: + // even though body may have offset, no compensation is required + offsets = { top: 0, bottom: 0, left: 0, right: 0 }; + } + + return offsets; +} diff --git a/test/bodyoffset-abs.html b/test/bodyoffset-abs.html new file mode 100644 index 00000000..62775272 --- /dev/null +++ b/test/bodyoffset-abs.html @@ -0,0 +1,78 @@ + + +
+ +The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.
+The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.
+This box has a mouse following tooltip.
+Trap it in the bottom right corner of the viewport. It should flip out of the way. It should not flip if it only hits one edge.
+The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.
+The tooltips for the buttons below have a lot of text. The tooltip div is completely elastic for this demo. The tooltips should be properly placed when they render.
+This box has a mouse following tooltip.
+Trap it in the bottom right corner of the viewport. It should flip out of the way. It should not flip if it only hits one edge.
+