Skip to content

Commit

Permalink
Fix tooltip positioning with regard to viewports
Browse files Browse the repository at this point in the history
The getPosition function was simplified with a new function,
getViewportBounds, was added to specifically
tackle the cases where we’re looking for the viewport bounds.

'absolute' and 'fixed' tooltips are now matched against the screen
where other ones are made to fit inside the viewport element.

Fixes #14195
Closes #14226
  • Loading branch information
ericmartel authored and cvrebert committed Mar 23, 2015
1 parent e22a73f commit 88697c0
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 4 deletions.
37 changes: 37 additions & 0 deletions js/tests/unit/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,43 @@ $(function () {
$styles.remove()
})

QUnit.test('should not adjust position when scrolling the window', function (assert) {
assert.expect(2)
var styles = '<style>'
+ '.large-spacer { height: 3000px; }'
+ '</style>'
var $styles = $(styles).appendTo('head')

var $container = $('<div class="container-viewport"/>').appendTo(document.body)
var $target = $('<a href="#" rel="tooltip" title="tip"/>')
.appendTo($container)
.bootstrapTooltip({
placement: 'right',
viewport: 'body'
})
$('<div class="large-spacer"/>').appendTo($container)

$target.bootstrapTooltip('show')
var $tooltip = $container.find('.tooltip')
var $initialTop = Math.round($tooltip.offset().top)

$target.bootstrapTooltip('hide')

window.scrollTo(0, 2000)

$target.bootstrapTooltip('show')
$tooltip = $container.find('.tooltip')

assert.strictEqual(Math.round($tooltip.offset().top), $initialTop, 'position is the same after scrolling')
assert.strictEqual($(document).scrollTop(), 2000, 'document scrolled')

$target.bootstrapTooltip('hide')

$container.remove()
$styles.remove()
window.scrollTo(0, 0)
})

QUnit.test('should not error when trying to show an auto-placed tooltip that has been removed from the dom', function (assert) {
assert.expect(1)
var passed = true
Expand Down
26 changes: 22 additions & 4 deletions js/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@
}
var elOffset = isBody ? { top: 0, left: 0 } : $element.offset()
var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
var outerDims = isBody ? { width: $element.outerWidth(), height: $element.outerHeight() } : null

return $.extend({}, elRect, scroll, outerDims, elOffset)
}
Expand All @@ -357,16 +357,34 @@

}

Tooltip.prototype.getViewportBounds = function ($viewport) {
if ($viewport.is('body') && (/fixed|absolute/).test(this.$element.css('position'))) {
// fixed and absolute elements should be tested against the window
return this.getScreenSpaceBounds($viewport)
}

return $.extend({}, $viewport.offset(), { width: $viewport.outerWidth(), height: $viewport.outerHeight() })
}

Tooltip.prototype.getScreenSpaceBounds = function ($viewport) {
return {
top: $viewport.scrollTop(),
left: $viewport.scrollLeft(),
width: $(window).width(),
height: $(window).height()
}
}

Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
var delta = { top: 0, left: 0 }
if (!this.$viewport) return delta

var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
var viewportDimensions = this.getPosition(this.$viewport)
var viewportDimensions = this.getViewportBounds(this.$viewport)

if (/right|left/.test(placement)) {
var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
var topEdgeOffset = pos.top - viewportPadding
var bottomEdgeOffset = pos.top + viewportPadding + actualHeight
if (topEdgeOffset < viewportDimensions.top) { // top overflow
delta.top = viewportDimensions.top - topEdgeOffset
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
Expand Down

0 comments on commit 88697c0

Please sign in to comment.