Search code examples
jqueryhtmlcsstooltip

Dynamically centered tooltip isn't positioning correctly at first


I've created custom tooltips to show details about a 'title' element when the user hovers over it. There's only one element that acts as the tooltip itself, so even if there are more than one 'title' elements, the tooltip will jump around, showing details for the one currently moused over.

Desired behavior: The tooltip needs to pop up right above the center of its 'title' element, so that the arrow at the bottom of the tooltip lines up with the center of the title element.

Actual behavior: The tooltip is positioned about 30px off, the first time the user hovers over any given 'title' element. If the user mouses off the element, then back onto the same one, it will then be correct. Please see below snippet to see what I mean.

Why doesn't the tooltip get positioned correctly the very first time I roll over the 'title' element?

Also, if the text within the tooltips both only contain one line, it seems to work well.

$('.details').hide();
$('.title').mouseenter(function() {
  var el = $(this).attr('name'),
    eventPos = $('.title[name="' + el + '"]').offset(),
    yPos = (eventPos.top - ($('.details').outerHeight() + 5)) + 'px',
    xPos = ((eventPos.left + ($('.title[name="' + el + '"]').outerWidth() / 2)) - ($('.details').outerWidth() / 2)) + 'px';
  $('.details p').show().not('p:nth-child(' + el + ')').hide();
  $('.details')
    .css({
      top: yPos,
      left: xPos
    })
    .stop()
    .fadeIn('fast');
});

$('.title').mouseleave(function() {
  var el = $(this).attr('name');
  $('.details').fadeOut('fast');
})
.container {
  text-align: center;
}

.title {
  display: inline-block;
  padding: 10px;
  background-color: red;
  margin-top: 150px;
}

.details {
  position: absolute;
  top: -300px;
  background: #fefefe;
  border: 1px solid #aaa;
  border-radius: 5px;
  padding: 10px;
  z-index: 10;
  box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.3);
}

.details:after,
.details:before {
  top: 100%;
  left: 50%;
  border: solid transparent;
  content: " ";
  height: 0;
  width: 0;
  position: absolute;
  pointer-events: none;
}

.details:after {
  border-color: rgba(254, 254, 254, 0);
  border-top-color: #fefefe;
  border-width: 10px;
  margin-left: -10px;
}

.details:before {
  border-color: rgba(170, 170, 170, 0);
  border-top-color: #aaa;
  border-width: 11px;
  margin-left: -11px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
  <div class="title" name="1">
    Title element 1
  </div>
  <div class="title" name="2">
    Title element 2
  </div>
</div>

<div class="details" id="1">
  <p>
    These are details about the main topic, shown as a blurb on hover.
  </p>
  <p>
    This is a blurb with two lines.
    <br> Why isn't the blurb positioned correctly when you first hover over?
  </p>
</div>


Solution

  • What's happening is that when you call $('.details p').show().not('p:nth-child(' + el + ')').hide();, you are changing the outerHeight of the tooltip. However, your code is calculating xPos and yPos before the move occurs, so your tooltip is positioned as if both texts were showing. I moved this line to the top of the function (before you calculate position), and the tooltip works properly now :) (example below)

    $('.details').hide();
    $('.title').mouseenter(function() {
        var el = $(this).attr('name');
        $('.details p').show().not('p:nth-child(' + el + ')').hide();
        var eventPos = $('.title[name="' + el + '"]').offset(),
        yPos = (eventPos.top - ($('.details').outerHeight() + 5)) + 'px',
        xPos = ((eventPos.left + ($('.title[name="' + el + '"]').outerWidth() / 2)) - ($('.details').outerWidth() / 2)) + 'px';
      
      $('.details')
        .css({
          top: yPos,
          left: xPos
        })
        .stop()
        .fadeIn('fast');
    });
    
    $('.title').mouseleave(function() {
      var el = $(this).attr('name');
      $('.details').fadeOut('fast');
    })
    .container {
      text-align: center;
    }
    
    .title {
      display: inline-block;
      padding: 10px;
      background-color: red;
      margin-top: 150px;
    }
    
    .details {
      position: absolute;
      top: -300px;
      background: #fefefe;
      border: 1px solid #aaa;
      border-radius: 5px;
      padding: 10px;
      z-index: 10;
      box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.3);
    }
    
    .details:after,
    .details:before {
      top: 100%;
      left: 50%;
      border: solid transparent;
      content: " ";
      height: 0;
      width: 0;
      position: absolute;
      pointer-events: none;
    }
    
    .details:after {
      border-color: rgba(254, 254, 254, 0);
      border-top-color: #fefefe;
      border-width: 10px;
      margin-left: -10px;
    }
    
    .details:before {
      border-color: rgba(170, 170, 170, 0);
      border-top-color: #aaa;
      border-width: 11px;
      margin-left: -11px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="container">
      <div class="title" name="1">
        Title element 1
      </div>
      <div class="title" name="2">
        Title element 2
      </div>
    </div>
    
    <div class="details" id="1">
      <p>
        These are details about the main topic, shown as a blurb on hover.
      </p>
      <p>
        This is a blurb with two lines.
        <br> Why isn't the blurb positioned correctly when you first hover over?
      </p>
    </div>