Search code examples
csstwitter-bootstrap-3overflowtwitter-bootstrap-tooltip

How to position bootstrap tooltip correctly next to a partially hidden image


I have the folowing page using bootstrap tooltip that I can't position correctly The page has the following characteristics:

  • #outer-container is my page. It has fixed width.
  • There are many .inner-container. They have fixed width and overflow-x: auto.
  • .inner-containers are dynamically generated. (Not sure if it's relevant, so the following sample code is static)
  • Inside .inner-container divs there is arbitrary HTML that could include many <img> tags
  • Each <img> has a tooltip with it's alt text as title.
  • Images can be wider than their .inner-container div

Here is the snippet, you can see the problem when running the snippet in full page.

$("#outer-container").tooltip({
   selector: "img",
   placement: "right",
   title: function () { return $(this).attr("alt"); },
 });
#outer-container
{
  border: 1px solid;
  padding: 10px;
  width: 960px;
  margin-left: auto;
  margin-right: auto;
  overflow: auto;
}

.inner-container
{
  width: 600px;
  overflow-x: auto;
  border: 1px solid;
  margin-bottom : 10px;
  float: right;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>

<div id="outer-container">
  <div class="inner-container">
    <figure>
      <img src="http://i.imgur.com/mrXWW8S.png" alt="Man ALL IS LOŚ͖̩͇̗̪̏̈́T ALL I​S LOST the pon̷y he comes he c̶̮omes he comes the ich​or permeates all MY FACE MY FACE ᵒh god no NO NOO̼O​O NΘ stop the an​*̶͑̾̾​̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͖͉̗̩̳̟̍ͫͥͨe̠̅s ͎a̧͈͖r̽̾̈́͒͑e n​ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚​N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔̀ͅ" />
      <figcaption>
        Image originally posted by <a href="http://meta.stackexchange.com/questions/213769/work-is-hard-lets-color-the-walls/213975#213975">Travis J</a>
      </figcaption>
    </figure>
  </div>
  
  <div class="inner-container">
    <figure>
      <img src="https://i.sstatic.net/CThCe.png" alt="Sanely-sized stackoverflow logo" />
      <figcaption>Sanely-sized stackoverflow logo</figcaption>
    </figure>
  </div>
  
  <div class="inner-container">
    <figure>
      <img src="https://i.sstatic.net/MvHsz.jpg" alt="Insanely-long-sized stackoverflow logo" />
      <figcaption>Insanely-long-sized stackoverflow logo</figcaption>
    </figure>
  </div>
</div>

The above code works fine showing the tooltips but there is a little problem. The tooltip does not appear immediately positioned after the truncated image but after the place where the part of the image that is hidden ends so it appears too far form the image.

Large image: Wasted space

As you scroll .inner-containerto the right the tooltip gets closer to the img until it's next to it.

Large image fully scrolled: No more wasted space

The situation becomes even worse when the image is wider that the whole screen because now the tooltip is too far to the right and now it generates not only the expected scrollbar in .inner-container but also one on the whole page. The tooltip now is impossible to see because as soon as you try to scroll the tooltip fades.

Really long image: Tooltip outside the page and scrollbar

Now the question is.....

Is there any way to configure the bootstrap tooltip or to style it with css to make it always appear at the edge of the cropped image as in the second image as opposed to the "actual" but hidden edge?. Also when the image is shorter than the .inner-container the tooltip should appear next to the image and not at the right edge of the container


Solution

  • Thanks to @y34h for the idea, but I had to calculate the correct value of left property specifically by overriding the actual bootstrap js code.

    Here is the new Tooltip.getCalcultedOffset which is the function that calculates the absolute position of the tooltip:

        $.fn.tooltip.Constructor.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
        return placement === 'bottom' ? {
                top: pos.top + pos.height,
                left: pos.left + pos.width / 2 - actualWidth / 2 } :
            placement === 'top' ? {
                top: pos.top - actualHeight,
                left: pos.left + pos.width / 2 - actualWidth / 2 } :
            placement === 'left' ? {
                top: pos.top + pos.height / 2 - actualHeight / 2,
                left: pos.left - actualWidth } :
            /* placement == 'right' */ {
                top: pos.top + pos.height / 2 - actualHeight / 2,
                left:
                    /* begin fix */
                    Math.min(
                        pos.left + pos.width, //original left
                        $(".inner-container").offset().left + $(".inner-container")[0].clientWidth //max left
                    )
                    /* end fix */
            };
    };
    

    The actual change is enclosed in /* begin fix */ and /* end fix */

    Here is the complete working code:

    $.fn.tooltip.Constructor.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
            return placement === 'bottom' ? {
                    top: pos.top + pos.height,
                    left: pos.left + pos.width / 2 - actualWidth / 2 } :
                placement === 'top' ? {
                    top: pos.top - actualHeight,
                    left: pos.left + pos.width / 2 - actualWidth / 2 } :
                placement === 'left' ? {
                    top: pos.top + pos.height / 2 - actualHeight / 2,
                    left: pos.left - actualWidth } :
                /* placement == 'right' */ {
                    top: pos.top + pos.height / 2 - actualHeight / 2,
                    left:
                        /* begin fix */
                        Math.min(
                            pos.left + pos.width, //original left
                            $(".inner-container").offset().left + $(".inner-container")[0].clientWidth //max left
                        )
                        /* end fix */
                };
        };
    
    $("#outer-container").tooltip({
       selector: "img",
       placement: "right",
       title: function () { return $(this).attr("alt"); },
     });
    #outer-container
    {
      border: 1px solid;
      padding: 10px;
      width: 960px;
      margin-left: auto;
      margin-right: auto;
      overflow: auto;
    }
    
    .inner-container
    {
      width: 600px;
      overflow-x: auto;
      border: 1px solid;
      margin-bottom : 10px;
      float: right;
    }
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
    
    <div id="outer-container">
      <div class="inner-container">
        <figure>
          <img src="http://i.imgur.com/mrXWW8S.png" alt="Man ALL IS LOŚ͖̩͇̗̪̏̈́T ALL I​S LOST the pon̷y he comes he c̶̮omes he comes the ich​or permeates all MY FACE MY FACE ᵒh god no NO NOO̼O​O NΘ stop the an​*̶͑̾̾​̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͖͉̗̩̳̟̍ͫͥͨe̠̅s ͎a̧͈͖r̽̾̈́͒͑e n​ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚​N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔̀ͅ" />
          <figcaption>
            Image originally posted by <a href="http://meta.stackexchange.com/questions/213769/work-is-hard-lets-color-the-walls/213975#213975">Travis J</a>
          </figcaption>
        </figure>
      </div>
      
      <div class="inner-container">
        <figure>
          <img src="https://i.sstatic.net/CThCe.png" alt="Sanely-sized stackoverflow logo" />
          <figcaption>Sanely-sized stackoverflow logo</figcaption>
        </figure>
      </div>
      
      <div class="inner-container">
        <figure>
          <img src="https://i.sstatic.net/MvHsz.jpg" alt="Insanely-long-sized stackoverflow logo" />
          <figcaption>Insanely-long-sized stackoverflow logo</figcaption>
        </figure>
      </div>
    </div>