Hugo Sousa Hugo Sousa - 7 months ago 8
Javascript Question

Workaround for Bootstrap tooltip in multiple-line links

I know that in the official Bootstrap page it says that

white-space: nowrap;
should be added in multiple-line links with tooltips.

However, I am developing a Chrome extension and I want to add tooltips on any Web page, without changing its original layout. So, the proposed solution is not ideal for me.

I tried to manually set the placement of the tooltip. When this happens, it would return the leftmost position. So, I tried this solution, which returns the correct position (the beggining of the link, instead of the leftmost position).

The problem is that, either I set the placement
left
or
right
, it never positions as I want. If set to
right
, it shows outside the window, as it places starting on the rightmost position. If set to
left
, it also shows outside the window, as it places on the leftmost position.

See Fiddle

In this case, I would ideally want it to be placed left to the starting point of the link (in the first line).

Is there a workaround to make this happen?

Answer

You can either use mouse position as tooltip anchor or you can modify the tooltip javascript code to achieve this as in How to make popover appear where my mouse enters the hover target?. You can merge the bottom code with the one in the link to achieve both and choose the one you like.

In bootstrap-tooltip.js, replace (line 1602 I think) (Sorry for the poor format)

 Tooltip.prototype.getPosition 
 and
 Tooltip.prototype.getCalculatedOffset

functions with respectively

Tooltip.prototype.getPosition = function($element) {
  $element = $element || this.$element

  var el = $element[0]
  var isBody = el.tagName == 'BODY'

  var elRect = el.getBoundingClientRect()
  if (elRect.width == null) {
    // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
    elRect = $.extend({}, elRect, {
      width: elRect.right - elRect.left,
      height: elRect.bottom - elRect.top
    })
  }

  var rects = el.getClientRects();
  var firstRect = rects[0];
  var lastRect = rects[rects.length - 1];

  firstRect = $.extend({}, firstRect, {
    width: firstRect.right - firstRect.left,
    height: firstRect.bottom - firstRect.top
  })
  lastRect = $.extend({}, lastRect, {
    width: lastRect.right - lastRect.left,
    height: lastRect.bottom - lastRect.top
  })



  var elOffset = isBody ? {
    top: 0,
    left: 0
  } : $element.offset()
  var elScrollTop = elOffset.top - elRect.top;
  var elScrollLeft = elOffset.left - elRect.left;
  firstRect.top += elScrollTop;
  lastRect.top += elScrollTop;
  firstRect.left += elScrollLeft;
  lastRect.left += elScrollLeft;

  firstRect = {
    firstRect: firstRect
  };
  lastRect = {
    lastRect: lastRect
  };

  var scroll = {
    scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop()
  }
  var outerDims = isBody ? {
    width: $(window).width(),
    height: $(window).height()
  } : null

  return $.extend({}, elRect, scroll, outerDims, elOffset, firstRect, lastRect)
}

Tooltip.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 == 'textleft' ? {
      top: pos.firstRect.top + (pos.firstRect.height / 2) - actualHeight / 2,
      left: pos.firstRect.left - actualWidth
    } :
    placement == 'textright' ? {
      top: pos.lastRect.top + (pos.lastRect.height / 2) - actualHeight / 2,
      left: pos.lastRect.right
    } :
    /* placement == 'right' */
    {
      top: pos.top + pos.height / 2 - actualHeight / 2,
      left: pos.left + pos.width
    }
}

Now you can use placements "textleft" and "textright". This works for Bootstrap v3.3.6. It can be improved by not directly editing the bootstrap source but extending it (and it is somewhat dirty).

Also you need to add this to css for tooltip arrows.

.tooltip.textright {
  padding: 0 5px;
  margin-left: 3px;
}
.tooltip.textleft {
  padding: 0 5px;
  margin-left: -3px;
}

.tooltip.textleft .tooltip-arrow{
  top: 50%;
  right: 0;
  margin-top: -5px;
  border-width: 5px 0 5px 5px;
  border-left-color: #000;
}

.tooltip.textright .tooltip-arrow{
  top: 50%;
  left: 0;
  margin-top: -5px;
  border-width: 5px 5px 5px 0;
  border-right-color: #000;
}

Here is the fiddle: JSFiddle

Comments