多行链接中 Bootstrap 工具提示的解决方法

Workaround for Bootstrap tooltip in multiple-line links

我知道 official Bootstrap page 中说 white-space: nowrap; 应该添加到带有工具提示的多行 link 中。

但是,我正在开发 Chrome 扩展,我想在任何网页上添加工具提示,而不更改其原始布局。因此,建议的解决方案对我来说并不理想。

我尝试手动设置工具提示的位置。当发生这种情况时,它将 return 放在最左边的位置。所以,我尝试了 this solution,其中 return 是正确的位置(link 的开头,而不是最左边的位置)。

问题是,无论我设置位置 left 还是 right,它都不会按我想要的方式放置。如果设置为 right,它显示在 window 之外,因为它位于最右边的位置。如果设置为 left,它也会显示在 window 之外,因为它位于最左边的位置。

See Fiddle

在这种情况下,理想情况下,我希望它位于 link 的起点左侧(第一行)。

是否有解决方法来实现这种情况?

您可以使用鼠标位置作为工具提示锚点,也可以修改工具提示 javascript 代码来实现此目的,如 How to make popover appear where my mouse enters the hover target? 中所示。您可以将底部的代码与link中的代码合并,实现两者并选择您喜欢的代码。

在bootstrap-tooltip.js中,替换(我认为是第1602行)(格式不好请见谅)

 Tooltip.prototype.getPosition 
 and
 Tooltip.prototype.getCalculatedOffset

函数分别

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
    }
}

现在您可以使用展示位置 "textleft" 和 "textright"。 这适用于 Bootstrap v3.3.6。可以通过不直接编辑 bootstrap 源代码而是扩展它(它有点脏)来改进它。

您还需要将此添加到 css 以获得工具提示箭头。

.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;
}

这里是fiddle:JSFiddle

这是对@Gökhan Kurt 回答的改进。

他的解决方案将工具提示放在多行的左侧或右侧 link。但是,我想改进它并使其看起来更好。因此,当您将鼠标悬停在上方时,工具提示会出现在 link 的左侧;当您将鼠标悬停在底线时,工具提示会出现在 link.

的右侧

为此,我检测到用户悬停 link 的位置。如果它靠近 window 的右侧,则表示它是上线。否则,这是底线。如果您在工具提示选项中设置 container: 'body',这将起作用。

对于Bootstrapv3.3.6,tooltip插件源代码:

enter函数中获取鼠标位置:

if (obj instanceof $.Event) {
  self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
  var mousePos = { x: -1, y: -1 };
  mousePos.x = obj.pageX;
  mousePos.y = obj.pageY;
  window.mousePos = mousePos;
}

show 函数中清除位置 CSS class(因为每次悬停 link 时可能会有所不同)并设置新的位置取决于鼠标位置:

var isTextPlacement = /textleft|textright/.test(placement)
if(isTextPlacement){
    $tip.removeClass("textright");
    $tip.removeClass("textleft");
}

//390 is hardcoded value which is the max size of the tooltip, you may adjust to your case
if(placement == 'textleft' && window.mousePos.x < 390){
  placement = 'textright';
}else if(placement == 'textright' && window.mousePos.x > $(window).width() - 390){
  placement = 'textleft';
}

对于getCalculatedOffset,我使用了以下内容,如果无法应用textright,则应用textleft

  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
    var windowWidth = $(window).width();

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

就是这样。检查 JSFiddle(调整输出 window 以使 link 跨越两行并与@Gökhan Kurt 答案进行比较。您可能需要将其设置得更大,因为硬编码 390px).