question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Tooltip/Popover should reposition itself automatically when window is resized or otherwise

See original GitHub issue

Using Bootstrap in a client project, I had to fix a bug where a Popover was incorrectly positioned. The detail was that the mix of markup and JavaScript on the page created the popover, set the focus to the triggering field (causing the Popover to appear), but then other JavaScript modified the DOM (adding an alert to the top of the page) that shifted down the triggering field … and the Popover stayed positioned relative to the fields original position.

I also noticed that resizing the browser window also fails to reposition the Popover when, again, the location of the underlying field has changed.

My brief research on DOM events indicates that support for generic DOM tree modification events is spotty and buggy, but handling the window resize event would be sufficient for many people.

My approach to this is a bit of a hack (and has some Apache Tapestry details mixed in).

  /** Adds a new operation, "reposition", to Bootstrap's popover that
   *  re-positions the tooltip/popover if it is currently visible. This is used to handle
   *  changes to the DOM and window resizing.
   */
  jQuery.fn.popover.Constructor.prototype.reposition = function () {
    if (this.enabled && this.tip().hasClass("in")) {
      this.show();
    }
  }

… and in the code that creates the Popover:

  matches.popover(options);

  function doReposition() {
    matches.popover("reposition");
  }

  jQuery(window).resize(_.throttle(doReposition, 250));

  // Ideally, there would just be a way to handle this for anything that modifies the DOM.
  T5.sub(T5.events.ADD_ALERT, null, doReposition);

This works acceptibly; there’s a slighe fade in/fade out animation here that I’d prefer to avoid; it would be nice if the tooltip show() function could be broken up a little, so that the code that determines position could be invoked directly, without all the other stuff that show() does.

Issue Analytics

  • State:closed
  • Created 11 years ago
  • Comments:11 (4 by maintainers)

github_iconTop GitHub Comments

7reactions
mcharriscommented, Apr 4, 2017

Because of the location of the item that displays the tooltip I really needed something that that could recalculate the tooltip’s position. I do think that @hlship is absolutely right about this being broken up in the show function. I took the show function and eliminated everything that I thought was not needed for a recalc.

Here is what I have:

  $.fn.tooltip.Constructor.prototype.recalculatePosition = function(){
    var $tip = this.tip()
    if($tip.is(':visible')){
      var placement = typeof this.options.placement == 'function' ?
        this.options.placement.call(this, $tip[0], this.$element[0]) :
        this.options.placement

      var autoToken = /\s?auto?\s?/i
      var autoPlace = autoToken.test(placement)
      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'

      $tip.addClass(placement)

      var pos          = this.getPosition()
      var actualWidth  = $tip[0].offsetWidth
      var actualHeight = $tip[0].offsetHeight

      if (autoPlace) {
        var orgPlacement = placement
        var viewportDim = this.getPosition(this.$viewport)

        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
                    placement

        $tip.removeClass(orgPlacement).addClass(placement)
      }

      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
      this.applyPlacement(calculatedOffset, placement)
    }

Using this I was able to then call the following anywhere in my code to recalculate the tooltip’s position

$('<selector to element with tooltip>').tooltip('recalculatePosition');

With that you can attach it to event listeners, etc. I hope this helps someone. I couldn’t use this show function since it triggered the transition animation. This in my mind was a better solution.

As for popovers, I’m sure this could be adapted for it.

1reaction
fluidbluecommented, Jun 3, 2013

Another hack/workaround (avoiding CSS transitions during resize):

$(window).resize(function()
{
    if ($(".popover").is(":visible"))
    {
        var popover = $(".popover");
        popover.addClass("noTransition");
        $("input:focus").popover('show');
        popover.removeClass("noTransition");
    }
});

CSS:

.noTransition
{
    -moz-transition: none !important;
    -webkit-transition: none !important;
    -o-transition: none !important;
    transition: none !important;
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

bootstrap tooltip\popover - solving inconsistent placement to ...
This works the first time you hover, but every time after that, it sets the top to be too high, so you can't...
Read more >
Popovers - Bootstrap
How to position the popover - auto | top | bottom | left | right. When auto is specified, it will dynamically reorient...
Read more >
Everything I Know About Positioning Poppers (Tooltips ...
Tagged with tooltip, popover, dropdown, positioning. ... just want to call a position() function that will do this hard work automatically.
Read more >
Bootstrap Tooltip - examples & tutorial
Triggering tooltips on hidden elements will not work. ... The tooltip position attempts to automatically change when a parent container has overflow: auto...
Read more >
Building a simple tooltip component that never goes off screen
We can easily build a tooltip for desktop screens using the :hover pseudo-class. The drawback with this approach is that we need a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found