如何在鼠标悬停时保持 Bootstrap 弹出框的显示?

135
我正在使用Bootstrap的气泡窗口来创建悬停卡片,显示用户信息,并在鼠标悬停在按钮上时触发它。我希望在悬停在气泡窗口上时保持其存在,但是当用户停止悬停在按钮上时,它就消失了。我该如何做到这一点?

我正在使用Bootstrap Popover制作悬浮卡片展示用户信息,并通过鼠标悬停在按钮上来触发它。我想在悬停在悬浮卡片上时让它保持显示,但用户停止悬停在按钮上时它会消失。我该怎么做?

$('#example').popover({
    html : true,
    trigger : 'manual',
    content : function() {
        return '<div class="box">Popover</div>';
    }
});

$(document).on('mouseover', '#example', function(){
    $('#example').popover('show');
});

$(document).on('mouseleave', '#example', function(){
    $('#example').popover('hide');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.js"></script>
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>

<a href="#" id="example" class="btn btn-danger" rel="popover" >hover for popover</a>


你想保持什么状态?我刚才将鼠标悬停在按钮上,它就一直保持打开状态了? - David Chase
3
好的,请提供需要翻译的内容。 - vikas devde
21个回答

202

以下是代码片段的测试:

根据提供的解决方案(由vikas提供),进行了一些小修改以适应我的用例。

  1. 当鼠标悬停在弹出框按钮上时,打开弹出框。
  2. 当鼠标悬停在弹出框中时,保持弹出框打开状态。
  3. 当鼠标离开弹出框按钮或弹出框时,关闭弹出框。

$(".pop").popover({
    trigger: "manual",
    html: true,
    animation: false
  })
  .on("mouseenter", function() {
    var _this = this;
    $(this).popover("show");
    $(".popover").on("mouseleave", function() {
      $(_this).popover('hide');
    });
  }).on("mouseleave", function() {
    var _this = this;
    setTimeout(function() {
      if (!$(".popover:hover").length) {
        $(_this).popover("hide");
      }
    }, 300);
  });
<!DOCTYPE html>
<html>

<head>
  <link data-require="bootstrap-css@*" data-semver="3.2.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
  <script data-require="jquery@*" data-semver="2.1.1" src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script data-require="bootstrap@*" data-semver="3.2.0" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.js"></script>

  <link rel="stylesheet" href="style.css" />

</head>

<body>
  <h2 class='text-primary'>Another Great "KISS" Bootstrap Popover example!</h2>
  <p class='text-muted'>KISS = Keep It Simple S....</p>

  <p class='text-primary'>Goal:</p>
  <ul>
    <li>Open popover on hover event for the popover button</li>
    <li>Keep popover open when hovering over the popover box</li>
    <li>Close popover on mouseleave for either the popover button, or the popover box.</li>
  </ul>

  <button type="button" class="btn btn-danger pop" data-container="body" data-toggle="popover" data-placement="right" data-content="Optional parameter: Skip if this was not requested<br>                                    A placement group is a logical grouping of instances within a single Availability                                     Zone. Using placement groups enables applications to get the full-bisection bandwidth                                     and low-latency network performance required for tightly coupled, node-to-node                                     communication typical of HPC applications.<br>                                    This only applies to cluster compute instances: cc2.8xlarge, cg1.4xlarge, cr1.8xlarge, hi1.4xlarge and hs1.8xlarge.<br>                                    More info: <a href=&quot;http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html&quot; target=&quot;_blank&quot;>Click here...</a>"
    data-original-title="" title="">
    HOVER OVER ME
    </button>
  <br><br>
  <button type="button" class="btn btn-info pop" data-container="body" data-toggle="popover" data-placement="right" data-content="Optional parameter: Skip if this was not requested<br>                                    A placement group is a logical grouping of instances within a single Availability                                     Zone. Using placement groups enables applications to get the full-bisection bandwidth                                     and low-latency network performance required for tightly coupled, node-to-node                                     communication typical of HPC applications.<br>                                    This only applies to cluster compute instances: cc2.8xlarge, cg1.4xlarge, cr1.8xlarge, hi1.4xlarge and hs1.8xlarge.<br>                                    More info: <a href=&quot;http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html&quot; target=&quot;_blank&quot;>Click here...</a>"
    data-original-title="" title="">
    HOVER OVER ME... Again!
    </button><br><br>
  <button type="button" class="btn btn-success pop" data-container="body" data-toggle="popover" data-placement="right" data-content="Optional parameter: Skip if this was not requested<br>                                    A placement group is a logical grouping of instances within a single Availability                                     Zone. Using placement groups enables applications to get the full-bisection bandwidth                                     and low-latency network performance required for tightly coupled, node-to-node                                     communication typical of HPC applications.<br>                                    This only applies to cluster compute instances: cc2.8xlarge, cg1.4xlarge, cr1.8xlarge, hi1.4xlarge and hs1.8xlarge.<br>                                    More info: <a href=&quot;http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html&quot; target=&quot;_blank&quot;>Click here...</a>"
    data-original-title="" title="">
    Okay one more time... !
    </button>
  <br><br>
  <p class='text-info'>Hope that helps you... Drove me crazy for a while</p>
  <script src="script.js"></script>
</body>

</html>


这个完美地运行了,我注意到你第二个 $(_this).popover("hide") 中缺少了一个 ;。但是非常感谢,它如此简单干净! - scapegoat17
3
这个答案很棒。截至2015年5月,在BS3上运行良好 ^^ - degenerate
1
我在表格中使用了它,并在选项中添加了 container: 'body',因为它使单元格发生了位移。非常好的答案! - Alexander Derck
很遗憾,在火狐浏览器里这个功能无法正常使用。但是在谷歌浏览器中可以正常工作。在火狐浏览器中,当您点击弹出框的链接元素时,弹出框会消失。只有当您悬停在弹出框本身上时,它才会保持打开状态。 - goldlife
它在Firefox中可以正常工作。即使在FF版本54.0上再次测试,它仍然可以正常工作。也许您正在寻找与我尝试解决的不同功能。 - Okezie
显示剩余5条评论

86

我找到了另一种解决方案...这是代码:

    $('.selector').popover({
        html: true,
        trigger: 'manual',
        container: $(this).attr('id'),
        placement: 'top',
        content: function () {
            $return = '<div class="hover-hovercard"></div>';
        }
    }).on("mouseenter", function () {
        var _this = this;
        $(this).popover("show");
        $(this).siblings(".popover").on("mouseleave", function () {
            $(_this).popover('hide');
        });
    }).on("mouseleave", function () {
        var _this = this;
        setTimeout(function () {
            if (!$(".popover:hover").length) {
                $(_this).popover("hide")
            }
        }, 100);
    });

6
重要的是添加animation: false,否则重复将鼠标移动到链接上会导致它无法正常工作。 - jasop
5
我对 @vikas(https://gist.github.com/Nitrodist/7913848)的代码有一个小修改建议。它会在50毫秒后重新检查条件,以防止其保持打开状态。也就是说,它会每50毫秒连续重新检查一次。 - Nitrodist
3
如何调整此内容以使其适用于刚添加到文档中的实时元素? - williamsowen
1
很遗憾,这些解决方案都没有起作用。我感谢大家的努力和贡献。这些都是朝着正确方向迈出的步伐。然而,正确答案不应该被标记为正确答案,我不知道如何改变这一点,特别是对于这样一个古老的话题。我已经编写了一个看起来很强大的解决方案。我仍在测试中,以平滑一些边缘。一旦我对它有了100%的信心,我将继续进行。 - asiby

28

这是我的看法:http://jsfiddle.net/WojtekKruszewski/Zf3m7/22/

有时当你斜着移动鼠标从弹出框触发器到实际的弹出框内容时,你会停留在下面的元素上方。我想处理这种情况 - 只要在超时时间到达之前到达了弹出框内容,你就安全了(弹出框不会消失)。这需要使用delay选项。

此技巧基本上覆盖了Popover的leave函数,但调用了原始函数(启动计时器隐藏弹出框)。然后它将一次性监听器附加到mouseenter 弹出框内容元素上。

如果鼠标进入弹出框,则计时器将被清除。然后它将侦听mouseleave弹出框,并在触发时调用原始离开函数,以便可以启动隐藏计时器。

var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj){
  var self = obj instanceof this.constructor ?
    obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
  var container, timeout;

  originalLeave.call(this, obj);

  if(obj.currentTarget) {
    container = $(obj.currentTarget).siblings('.popover')
    timeout = self.timeout;
    container.one('mouseenter', function(){
      //We entered the actual popover – call off the dogs
      clearTimeout(timeout);
      //Let's monitor popover content instead
      container.one('mouseleave', function(){
        $.fn.popover.Constructor.prototype.leave.call(self, self);
      });
    })
  }
};

5
使用container = self.$tip;可以改善查找容器的效率,这样即使设置了container属性,也能找到弹出框。这里有一个示例:http://jsfiddle.net/dennis_c/xJc65/ - dbroeks
3
@pferrel,我已经在@Wojtek_Kruszewski的fiddle中解决了这个问题:http://jsfiddle.net/HugeHugh/pN26d/。请查看在调用`originalShow()`之前检查`if (!thisTip.is(':visible'))`的部分。 - FirstVertex
1
如果弹出框使用选项 container: 'body', 进行初始化,则此解决方案将无法按预期工作。变量 container 需要替换为 self.$tip。有关更多详细信息,请查看我的答案:https://dev59.com/MGQo5IYBdhLWcg3whPrI#28731847 - Rubens Mariuzzo
1
太棒了。这适用于使用“selector”参数,而其他答案则不适用。 - jetlej
1
这是一个改进版本,修复了当离开并重新进入提示时仍然隐藏它的错误,并修复了提示附加到主体时的情况。http://jsfiddle.net/Zf3m7/1499 - Zoltán Tamási
显示剩余2条评论

23

我认为一个简单的方法是这样的:

$('.popover').each(function () {
                    var $this = $(this);
                    $this.popover({
                        trigger: 'hover',
                        content: 'Content Here',
                        container: $this
                    })
                });

这种方式会在目标元素内部创建弹出窗口,因此当您将鼠标悬停在弹出窗口上时,它仍位于该元素之上。Bootstrap 3.3.2能够很好地与此配合使用。旧版本可能会存在一些动画问题,因此您可能需要禁用"animation:false"。


我知道这个帖子有点老了,但在我看来,这是最好、最干净的解决方案,应该排名更高。唯一的注意事项是,如果你将弹出框的位置(以一种奇怪的方式)“远离”触发元素,这个解决方案会失效。但只要两者之间的距离为零(例如它们重叠),它就能正常工作,而且不需要任何自定义JS。谢谢! - JohnGalt
1
这是目前为止最好、最干净、最简单的解决方案。应该排名更高!我添加了 delay: { "hide": 400 } 来在隐藏之前增加一些延迟,效果非常好! - coorasse

15

我使用了触发器设置为hover,并将容器设置为#element,最后将box的位置添加到right

你的设置应该如下所示:

$('#example').popover({
    html: true,
    trigger: 'hover',
    container: '#example',
    placement: 'right',
    content: function () {
        return '<div class="box"></div>';
    }
});

请检查下列 jsfiddle,#example 的 CSS 样式需要添加 position:relative;

https://jsfiddle.net/9qn6pw4p/1/

已编辑

这个 fiddle 中的两个链接都能正常工作: http://jsfiddle.net/davidchase03/FQE57/4/


那为什么不把 a 改成 button 呢,这样就没关系了?并且把 .box 的光标改成默认的... - David Chase
我现在不确定问题出在哪里?你的问题是在悬停时保持弹出框打开吗? - David Chase
我现在有解决方案了,我可以用<span>标签包围链接,然后我将在这个<span>标签上调用弹出框,而不是在<a>标签上... 这里是可工作的 http://jsfiddle.net/testtracker/FQE57/6/ - vikas devde
3
没有一个 jsfiddle 对我有效。Chrome Mar 20 2014. - pferrel
@DavidChase,你能给我展示一下如何开始做吗?如何使用ajax从服务器获取数据?我只想知道如何开始,因为我不确定,请指教。 - luis
显示剩余8条评论

12

这是我想出的一个解决方案,看起来效果很好,同时还允许您使用正常的Bootstrap实现来打开所有弹出框。

原始示例: https://jsfiddle.net/eXpressive/hfear592/

移植到本问题:

<a href="#" id="example" class="btn btn-danger" rel="popover" >hover for popover</a>

$('#example').popover({
    html : true,
    trigger : 'hover',
    content : function() {
        return '<div class="box"></div>';
    }
}).on('hide.bs.popover', function () {
    if ($(".popover:hover").length) {
      return false;
    }                
}); 

$('body').on('mouseleave', '.popover', function(){
    $('.popover').popover('hide');
});

1
这是最好的答案!它避免了设置container,在某些情况下可能不可行/不希望(例如当弹出框容器必须是其他东西时,例如为了逃避父级的overflow:hidden)。 - nirvana-msu

7

我是如何使用Bootstrap Popover与网络上其他内容的帮助进行的。动态获取网站上显示的各种产品的标题和内容。每个产品或弹出窗口都有唯一的ID。在退出产品($ this.pop)或弹出窗口时,弹出窗口将消失。超时用于在通过产品而不是弹出窗口退出时显示弹出窗口。

$(".pop").each(function () {
        var $pElem = $(this);
        $pElem.popover(
            {
                html: true,
                trigger: "manual",
                title: getPopoverTitle($pElem.attr("id")),
                content: getPopoverContent($pElem.attr("id")),
                container: 'body',
                animation:false
            }
        );
    }).on("mouseenter", function () {
        var _this = this;
        $(this).popover("show");
        console.log("mouse entered");
        $(".popover").on("mouseleave", function () {
            $(_this).popover('hide');
        });
    }).on("mouseleave", function () {
        var _this = this;
        setTimeout(function () {
            if (!$(".popover:hover").length) {
                $(_this).popover("hide");
            }
        }, 100);
    });
    function getPopoverTitle(target) {
        return $("#" + target + "_content > h3.popover-title").html();
    };

    function getPopoverContent(target) {
        return $("#" + target + "_content > div.popover-content").html();
    };

这也适用于弹出窗口不是目标元素的子级的情况。+1 - Taha Paksu

6

我认为最好的方法是使用以下方式提供的方法:David Chase、Cu Ly和其他人指出,实现这个功能的最简单方法是使用container: $(this)属性,如下所示:

$(selectorString).each(function () {
  var $this = $(this);
  $this.popover({
    html: true,
    placement: "top",
    container: $this,
    trigger: "hover",
    title: "Popover",
    content: "Hey, you hovered on element"
  });
});
我想在这里指出,此时的弹出窗口将继承当前元素的所有属性。例如,如果您对.btn元素(bootstrap)执行此操作,您将无法在弹出窗口内选择文本。只是想记录一下,因为我花了相当长的时间来试图解决这个问题。

很遗憾Bootstrap不允许使用data-container="this",因此无法在没有额外的each()初始化的情况下进行。 - Wirone

1

对我来说,Vikas的答案完美地解决了我的问题,在此我还为延迟(显示/隐藏)添加了支持。

var popover = $('#example');
var options = {
    animation : true,
    html: true,
    trigger: 'manual',
    placement: 'right',
    delay: {show: 500, hide: 100}
};   
popover
    .popover(options)
    .on("mouseenter", function () {

        var t = this;
        var popover = $(this);    
        setTimeout(function () {

            if (popover.is(":hover")) {

                popover.popover("show");
                popover.siblings(".popover").on("mouseleave", function () {
                    $(t).popover('hide');
                });
            }
        }, options.delay.show);
    })
    .on("mouseleave", function () {
        var t = this;
        var popover = $(this);

        setTimeout(function () {
            if (popover.siblings(".popover").length && !popover.siblings(".popover").is(":hover")) {
                $(t).popover("hide")
            }
        }, options.delay.hide);
    });     

另外请注意,我已经修改了:

if (!$(".popover:hover").length) {

使用:

if (popover.siblings(".popover").length && !popover.siblings(".popover").is(":hover")) {

因此,它会准确地引用那个打开的弹出窗口,而不是其他任何一个(由于延迟,现在可能同时打开多个)


我在结尾处做的评论实际上是不正确的,当使用容器:body时,如果仍然需要使用Vikas的解决方案来处理那一行。 - user1993198

1
所选答案可行,但如果弹出框使用body作为容器进行初始化,则会失败。
$('a').popover({ container: 'body' });

根据所选择的答案,下面是需要放在使用弹出框之前的代码。

var originalLeave = $.fn.popover.Constructor.prototype.leave;
$.fn.popover.Constructor.prototype.leave = function(obj) {
    var self = obj instanceof this.constructor ? obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type);
    originalLeave.call(this, obj);

    if (obj.currentTarget) {
        self.$tip.one('mouseenter', function() {
            clearTimeout(self.timeout);
            self.$tip.one('mouseleave', function() {
                $.fn.popover.Constructor.prototype.leave.call(self, self);
            });
        })
    }
};

使用self.$tip而不是遍历DOM来期望弹出框始终是元素的同级,这种变化很小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接