[jquery] Changing the position of Bootstrap popovers based on the popover's X position in relation to window edge?

I have scoured the Internet far and wide and while I found this stackoverflow post insightful Is it possible to change the position of Bootstrap popovers based on screen width?, it still didn't answer my question, likely due to my trouble understanding position/offset.

Here's what I'm trying to do. I want the Twitter Bootstap Popover position to be RIGHT unless the hotspot popover will be positioned beyond the viewport, then I want it to be positioned LEFT. I'm making an iPad web app that has hot spots, when you push on a hotspot, information appears but I don't want it to appear outside of the viewport when horizontal scrolling is locked.

I'm thinking it'd go something like this:

$('.infopoint').popover({
        trigger:'hover',
        animation: false,
        placement: wheretoplace 
    });
    function wheretoplace(){
        var myLeft = $(this).offset.left;

        if (myLeft<500) return 'left';
        return 'right';
    }

Thoughts? Thanks!

This question is related to jquery twitter-bootstrap offset popover

The answer is


$scope.popoverPostion = function(event){
    $scope.pos = '';

       if(event.x <= 207 && event.x >= 140){
           $scope.pos = 'top';
           event.x = '';
       }
       else if(event.x < 140){
           $scope.pos = 'top-left';
           event.x = '';
       }
       else if(event.x > 207){
           $scope.pos = 'top-right';
           event.x = '';
       }

};

You can use auto in data placement like data-placement="auto left". It will automatic adjust according to your screen size and default placement will be left.


You can also try this one if u need it in almost all places in the page.

U can configure it in a general way then just use it.

In this way u can also use HTML element and anything u want :)

_x000D_
_x000D_
$(document).ready(function()_x000D_
                  {_x000D_
  var options =_x000D_
      {_x000D_
        placement: function (context, source)_x000D_
        {_x000D_
          var position = $(source).position();_x000D_
          var content_width = 515;  //Can be found from a JS function for more dynamic output_x000D_
          var content_height = 110;  //Can be found from a JS function for more dynamic output_x000D_
_x000D_
          if (position.left > content_width)_x000D_
          {_x000D_
            return "left";_x000D_
          }_x000D_
_x000D_
          if (position.left < content_width)_x000D_
          {_x000D_
            return "right";_x000D_
          }_x000D_
_x000D_
          if (position.top < content_height)_x000D_
          {_x000D_
            return "bottom";_x000D_
          }_x000D_
_x000D_
          return "top";_x000D_
        }_x000D_
        , trigger: "hover"_x000D_
        , animation: "true"_x000D_
        , html:"true"_x000D_
      };_x000D_
  $('[data-toggle="popover"]').popover(options);_x000D_
});
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>_x000D_
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>_x000D_
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>_x000D_
_x000D_
_x000D_
<div class="container">_x000D_
 <h3>Popover Example</h3>_x000D_
 <a id="try_ppover" href="#" data-toggle="popover" title="Popover HTML Header" data-content="<div class='jumbotron'>Some HTML content inside the popover</div>">Toggle popover</a>_x000D_
</div>
_x000D_
_x000D_
_x000D_

Addition


For setting more options, u can go here.

Even more can be found here.

Update


If u want the popup after click, u can change the JS option to trigger: "click" like this-

        return ..;
    }
    ......
    , trigger: "click"
    ......
};

U also can customize it in HTML ading data-trigger="click" like this-

<a id="try_ppover" href="#" data-toggle="popover" data-trigger="click" title="Popover Header" data-content="<div class='jumbotron'>Some content inside the popover</div>">Toggle popover</a>

I think it will be more oriented code and more re-usable and more helpful to all :).


For Bootstrap 3, there is

placement: 'auto right'

which is easier. By default, it will be right, but if the element is located in the right side of the screen, the popover will be left. So it should be:

$('.infopoint').popover({
   trigger:'hover',
   animation: false,
   placement: 'auto right'
});

So I figured out a way to do this effectively. For my particular project I'm building an iPad website so I knew exactly what the pixel X/Y value would be to reach the edge of the screen. Your thisX and thisY values will vary.

Since the popovers are being placed by inline styling anyway, I simply grab the left and top values for each link to decide which direction the popover should be. This is done by appending html5 data- values that .popover() uses upon execution.

if ($('.infopoint').length>0){
    $('.infopoint').each(function(){
        var thisX = $(this).css('left').replace('px','');
        var thisY = $(this).css('top').replace('px','');
        if (thisX > 515)
            $(this).attr('data-placement','left');
        if (thisX < 515)
            $(this).attr('data-placement','right');
        if (thisY > 480)
            $(this).attr('data-placement','top');
        if (thisY < 110)
            $(this).attr('data-placement','bottom');
    });
    $('.infopoint').popover({
        trigger:'hover',
        animation: false,
        html: true
    });
}

Any comments/improvements are welcome but this gets the job done if you're willing to put the values in manually.


I solved my problem in AngularJS as follows:

var configPopOver = {
        animation: 500,
        container: 'body',
        placement: function (context, source) {
                    var elBounding = source.getBoundingClientRect();
                    var pageWidth = angular.element('body')[0].clientWidth
                    var pageHeith = angular.element('body')[0].clientHeith

                    if (elBounding.left > (pageWidth*0.34) && elBounding.width < (pageWidth*0.67)) {
                        return "left";
                    }

                    if (elBounding.left < (pageWidth*0.34) && elBounding.width < (pageWidth*0.67)) {
                        return "right";
                    }

                    if (elBounding.top < 110){
                        return "bottom";
                    }

                    return "top";
                },
        html: true
    };

This function do the position of Bootstrap popover float to the best position, based on element position.


You can also try this one,

==> Get target/source position in viewport
==> Check whether space from bottom is less that your tooltip height
==> If space from bottom is less that your tooltip height, then just scroll page up

placement: function(context, source){
  //get target/source position in viewport
  var bounds = $(source)[0].getBoundingClientRect();
      winHeight = $(window).height();

  //check wheather space from bottom is less that your tooltip height
  var rBottom = winHeight - bounds.bottom;

  //Consider 180 == your Tooltip height
  //if space from bottom is less that your tooltip height, this will scrolls page up
  //We are keeping tooltip position is fixed(at bottom)

  if (rBottom < 180){
    $('html, body').animate({ scrollTop: $(document).scrollTop()+180 }, 'slow');
  }

  return "bottom";
}

bchhun's answer got me on the right track, but I wanted to check for actual space available between the source and the viewport edge. I also wanted to respect the data-placement attribute as a preference with appropriate fallbacks if there wasn't enough space. That way "right" would always go right unless there wasn't enough space for the popover to show on the right side, for example. This was the way I handled it. It works for me, but it feels a bit cumbersome. If anyone has any ideas for a cleaner, more concise solution, I'd be interested to see it.

var options = {
  placement: function (context, source) {
    var $win, $source, winWidth, popoverWidth, popoverHeight, offset, toRight, toLeft, placement, scrollTop;

    $win = $(window);
    $source = $(source);
    placement = $source.attr('data-placement');
    popoverWidth = 400;
    popoverHeight = 110;
    offset = $source.offset();

    // Check for horizontal positioning and try to use it.
    if (placement.match(/^right|left$/)) {
      winWidth = $win.width();
      toRight = winWidth - offset.left - source.offsetWidth;
      toLeft = offset.left;

      if (placement === 'left') {
        if (toLeft > popoverWidth) {
          return 'left';
        }
        else if (toRight > popoverWidth) {
          return 'right';
        }
      }
      else {
        if (toRight > popoverWidth) {
          return 'right';
        }
        else if (toLeft > popoverWidth) {
          return 'left';
        }
      }
    }

    // Handle vertical positioning.
    scrollTop = $win.scrollTop();
    if (placement === 'bottom') {
      if (($win.height() + scrollTop) - (offset.top + source.offsetHeight) > popoverHeight) {
        return 'bottom';
      }
      return 'top';
    }
    else {
      if (offset.top - scrollTop > popoverHeight) {
        return 'top';
      }
      return 'bottom';
    }
  },
  trigger: 'click'
};
$('.infopoint').popover(options);

Based on the documentation you should be able to use auto in combination with the preferred placement e.g. auto left

http://getbootstrap.com/javascript/#popovers: "When "auto" is specified, it will dynamically reorient the popover. For example, if placement is "auto left", the tooltip will display to the left when possible, otherwise it will display right."

I was trying to do the same thing and then realised that this functionality already existed.


In addition to bchhun's great answer, if you want absoulte positioning, you can do this

var options = {
    placement: function (context, source) {
         setTimeout(function () {
               $(context).css('top',(source.getBoundingClientRect().top+ 500) + 'px')
         },0)
         return "top";
    },
    trigger: "click"
};
$(".infopoint").popover(options);

Examples related to jquery

How to make a variable accessible outside a function? Jquery assiging class to th in a table Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Getting all files in directory with ajax Bootstrap 4 multiselect dropdown Cross-Origin Read Blocking (CORB) bootstrap 4 file input doesn't show the file name Jquery AJAX: No 'Access-Control-Allow-Origin' header is present on the requested resource how to remove json object key and value.?

Examples related to twitter-bootstrap

Bootstrap 4: responsive sidebar menu to top navbar CSS class for pointer cursor How to install popper.js with Bootstrap 4? Change arrow colors in Bootstraps carousel Search input with an icon Bootstrap 4 bootstrap 4 responsive utilities visible / hidden xs sm lg not working bootstrap.min.js:6 Uncaught Error: Bootstrap dropdown require Popper.js Bootstrap 4 - Inline List? Bootstrap 4, how to make a col have a height of 100%? Bootstrap 4: Multilevel Dropdown Inside Navigation

Examples related to offset

Laravel Eloquent limit and offset Get div's offsetTop positions in React Bootstrap 3: Offset isn't working? How to fix Warning Illegal string offset in PHP Pagination using MySQL LIMIT, OFFSET How do I get the offset().top value of an element without using jQuery? Get position/offset of element relative to a parent container? offsetting an html anchor to adjust for fixed header Changing the position of Bootstrap popovers based on the popover's X position in relation to window edge? Can you control how an SVG's stroke-width is drawn?

Examples related to popover

Changing the width of Bootstrap popover bootstrap popover not showing on top of all elements How to insert close button in popover for Bootstrap HTML inside Twitter Bootstrap popover Contain form within a bootstrap popover? How to dismiss a Twitter Bootstrap popover by clicking outside? How do I use popover from Twitter Bootstrap to display an image? How to position a Bootstrap popover? Changing the position of Bootstrap popovers based on the popover's X position in relation to window edge? Is it possible to use a div as content for Twitter's Popover