如何通过点击外部区域关闭Twitter Bootstrap的弹出框?

311

我们能否使弹出框与模态框一样可以通过在外部单击来关闭呢?

不幸的是,我不能只使用真正的模态框来替换弹出框,因为模态框意味着position:fixed,这将不再是弹出框了。 :(


3
类似问题:https://dev59.com/zWox5IYBdhLWcg3wznk0 - Sherbrow
尝试这个 https://dev59.com/Em445IYBdhLWcg3w3N6z#40661543。不需要循环遍历父级元素。 - Tunn
data-trigger="hover"data-trigger="focus" 是关闭弹出框的内置替代方法,如果您不想使用切换。在我看来,data-trigger="hover" 提供了最好的用户体验...没有必要编写额外的 .js 代码... - Hooman Bahreini
41个回答

483

更新:稍微更健壮的解决方案:http://jsfiddle.net/mattdlockyer/C5GBU/72/

对于仅包含文本的按钮:

$('body').on('click', function (e) {
    //did not click a popover toggle or popover
    if ($(e.target).data('toggle') !== 'popover'
        && $(e.target).parents('.popover.in').length === 0) { 
        $('[data-toggle="popover"]').popover('hide');
    }
});

对于包含图标的按钮,请使用(此代码在Bootstrap 3.3.6中存在错误,请参见本答案下面的修复方法)

$('body').on('click', function (e) {
        //did not click a popover toggle, or icon in popover toggle, or popover
        if ($(e.target).data('toggle') !== 'popover'
            && $(e.target).parents('[data-toggle="popover"]').length === 0
            && $(e.target).parents('.popover.in').length === 0) { 
            $('[data-toggle="popover"]').popover('hide');
        }
    });

对于JS生成的弹出框,请使用'[data-original-title]'代替'[data-toggle="popover"]'

注意:上述解决方案允许同时打开多个弹出框。

请一次只打开一个弹出框:

更新:Bootstrap 3.0.x,请参见代码或示例http://jsfiddle.net/mattdlockyer/C5GBU/2/

$('body').on('click', function (e) {
    $('[data-toggle="popover"]').each(function () {
        //the 'is' for buttons that trigger popups
        //the 'has' for icons within a button that triggers a popup
        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
            $(this).popover('hide');
        }
    });
});

这处理已经打开但未被点击或其链接未被点击的弹出框的关闭。


更新: Bootstrap 3.3.6,查看 fiddle

修复了关闭后需要2次点击才能重新打开的问题。

$(document).on('click', function (e) {
    $('[data-toggle="popover"],[data-original-title]').each(function () {
        //the 'is' for buttons that trigger popups
        //the 'has' for icons within a button that triggers a popup
        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {                
            (($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false  // fix for BS 3.3.6
        }

    });
});

更新:利用之前的改进条件,实现了这个解决方案。修复了双击和幽灵气泡的问题:

$(document).on("shown.bs.popover",'[data-toggle="popover"]', function(){
    $(this).attr('someattr','1');
});
$(document).on("hidden.bs.popover",'[data-toggle="popover"]', function(){
    $(this).attr('someattr','0');
});
$(document).on('click', function (e) {
    $('[data-toggle="popover"],[data-original-title]').each(function () {
        //the 'is' for buttons that trigger popups
        //the 'has' for icons within a button that triggers a popup
        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
            if($(this).attr('someattr')=="1"){
                $(this).popover("toggle");
            }
        }
    });
});

6
在激活弹出框后(以及后续的隐藏操作),弹出框并没有完全隐藏; 它只是不可见。问题在于,位于不可见但存在的弹出框下方的内容无法被点击或悬停。此问题发生在最新版本的Chrome浏览器和最新版本的bootstrap 3 .js上(可能也会在其他浏览器上发生,但由于这个解决方法应该总是需要的,所以我没有去检查)。 - ravb79
6
我使用'[data-original-title]'作为选择器,而不是'[data-toggle="popover"]',因为它无法与JavaScript生成的弹出窗口一起使用。 - Nathan
4
有人知道为什么这个解决方案在最新版本的Bootstrap中无法使用吗?以下是问题描述:点击按钮显示弹出框,然后点击页面其他区域关闭弹出框,再点击按钮显示弹出框,但是弹出框没有显示。第一次失败后,如果再次点击它,它就会显示出来。这非常奇怪。 - JTunney
3
@JTunney 我正在运行BS 3.3.6,仍然看到这种情况,在关闭一个弹出框后需要单击两次才能打开另一个。 - sersun
3
难以置信,我几年前就实施了你的解决方案,但在升级后它不起作用了。我无法相信我又看到了你的帖子,并且不断更新解决方案。我希望我能够加倍使用你的答案。期待 V4 推出时查看你的解决方案。 - CalebC
显示剩余27条评论

86
$('html').on('mouseup', function(e) {
    if(!$(e.target).closest('.popover').length) {
        $('.popover').each(function(){
            $(this.previousSibling).popover('hide');
        });
    }
});

如果您在弹出窗口以外的任何地方单击,这将关闭所有弹出窗口。

Bootstrap 4.1 更新

$("html").on("mouseup", function (e) {
    var l = $(e.target);
    if (l[0].className.indexOf("popover") == -1) {
        $(".popover").each(function () {
            $(this).popover("hide");
        });
    }
});

我在触发弹出框的按钮(pop-btn)上添加了一个类,以便它不被包含在内... 如果(!$(e.target).closest('.popover').length && !$(e.target).closest('.btn').hasClass('pop-btn')) - mozgras
2
带有三个弹出式按钮的代码存在问题。在某些情况下,我无法点击按钮,而按钮会闪烁。 - OpenCode
1
无法使这段代码工作...请查看这个fiddle,并在您的答案中添加一个fiddle。http://jsfiddle.net/C5GBU/102/ - mattdlockyer
非常适合我。其他答案在我的“外部点击”打开新的弹出窗口时会产生副作用。 - Facio Ratio
这很好用,但需要有一种方式来使其适应这样的情况:如果你点击弹出窗口中的内容,则不会关闭。例如,如果你点击弹出窗口内一个<b>标签里的文本... - Ben in CA
支持在子HTML标签内点击://如果您在弹出窗口以外的任何地方单击,则关闭所有弹出窗口 $(“html”).on(“mouseup”,function(e){ var click_value = $(e.target)[0] .className.indexOf(“popover”); var click_parent = $(e.target)[0] .parentNode.className.indexOf(“popover”); if(click_value == -1 && click_parent!= 0){ $(“.popover”).each(function(){ $(this).popover(“hide”); }); } }); - Ben in CA

43

最简单、最安全的版本,适用于任何 Bootstrap 版本。

Demo: http://jsfiddle.net/guya/24mmM/

Demo 2: 在单击弹出框内容区域内时不关闭它:http://jsfiddle.net/guya/fjZja/

Demo 3: 多个弹出框:http://jsfiddle.net/guya/6YCjW/


只需调用这一行代码即可关闭所有弹出框:

$('[data-original-title]').popover('hide');

使用此代码在点击外部时关闭所有弹出式窗口:

$('html').on('click', function(e) {
  if (typeof $(e.target).data('original-title') == 'undefined') {
    $('[data-original-title]').popover('hide');
  }
});

上述代码段在 body 元素上绑定了一个点击事件。当用户单击弹出框时,它将像正常情况下一样运行。当用户单击非弹出框元素时,它将关闭所有弹出框。

它还适用于通过 JavaScript 启动的弹出框,而不是其他一些无法运行的示例(请参见演示)。

如果您不希望在单击弹出框内容时关闭,请使用此代码(请参见第二个演示链接):

$('html').on('click', function(e) {
  if (typeof $(e.target).data('original-title') == 'undefined' && !$(e.target).parents().is('.popover.in')) {
    $('[data-original-title]').popover('hide');
  }
});

3
我遇到了类似的问题,在Bootstrap 3中这个方法有效。 - wsams
如果您将弹出窗口靠近以使它们重叠,当您单击外部某处隐藏弹出窗口时,其中一个链接将停止工作。请参考:http://jsfiddle.net/qjcuyksb/1/ - Sandeep Giri
1
当在弹出框中使用bootstrap-datepicker时,最新版本无法正常工作。 - dbinott
1
我最喜欢这个解决方案,因为被接受的答案开始变得有些资源占用过多,弹出了30个左右的弹窗。 - David G
1
也许 !$(e.target).closest('.popover.in').length!$(e.target).parents().is('.popover.in') 更有效率。 - joeytwiddle
显示剩余3条评论

22

所谓的高票解决方案都不能正确地为我工作。每个解决方案在第一次打开和关闭(通过单击其他元素)弹出框后,都会导致一个错误,之后它就再也无法打开,直到你点击触发链接两次才能再次打开。

因此,我稍微修改了它:

$(document).on('click', function (e) {
    var
        $popover,
        $target = $(e.target);

    //do nothing if there was a click on popover content
    if ($target.hasClass('popover') || $target.closest('.popover').length) {
        return;
    }

    $('[data-toggle="popover"]').each(function () {
        $popover = $(this);

        if (!$popover.is(e.target) &&
            $popover.has(e.target).length === 0 &&
            $('.popover').has(e.target).length === 0)
        {
            $popover.popover('hide');
        } else {
            //fixes issue described above
            $popover.popover('toggle');
        }
    });
})

1
不错,它对我有用。顺便说一下,在最后一个}之后,你忘记了一个);。 - Merlin
1
遇到了与第二次点击相关的同样问题。这应该是故障保护答案! - Felipe Leão
我也尝试了上面的先前解决方案,但作为给那些在2016年寻找解决方案的人的指导,这是一个更好的解决方案。 - dariru
顶级答案,按照广告运作。正如所指出的那样,其他人没有。这应该是顶级答案。 - duggi
1
除了我没有使用"data-toggel =“popover”"之外,它完美地工作。但是,您可以指定与您的弹出窗口触发元素匹配的任何选择器。这是一个不错的解决方案,也是唯一一个为我解决了双击问题的方法。 - shock_gone_wild
这太完美了! - Kam

21

使用Bootstrap 2.3.2,您可以将触发器设置为“focus”,它就可以正常工作:

$('#el').popover({trigger:'focus'});

1
+1,但重要的一点是:如果您在接受答案时再次单击按钮或锚点,则不会关闭弹出窗口。而接受的答案则会关闭。 - Christian Gollhardt

18

这基本上并不太复杂,但需要进行一些检查以避免故障。

演示(jsfiddle)

var $poped = $('someselector');

// Trigger for the popover
$poped.each(function() {
    var $this = $(this);
    $this.on('hover',function() {
            var popover = $this.data('popover');
            var shown = popover && popover.tip().is(':visible');
            if(shown) return;        // Avoids flashing
            $this.popover('show');
    });
});

// Trigger for the hiding
 $('html').on('click.popover.data-api',function() {
    $poped.popover('hide');
});

2
这意味着单击任何地方都可以关闭模态框,而不仅仅是在外部点击。 - hienbt88
能否通过点击而不是悬停来使用 popover() 实现这个功能? - Zaki Aziz
3
当然,但您需要在传递给单击处理程序的事件上调用 stopPropagation()(否则,hiding 处理程序会立即隐藏弹出框)。演示(jsfiddle) - Sherbrow
我在下面的代码中实现了相同的功能,但使用的代码要少得多。这个答案有点浮夸,对于这个问题来说有些荒谬...他只是想在点击外部时关闭弹出窗口...这太过于复杂和丑陋了! - mattdlockyer
2
更正一下,我相信我在更少的代码中实现了更好的功能。它假设您一次只想要一个弹出窗口可见。如果您喜欢这个,请在下面投票支持我的答案:http://jsfiddle.net/P3qRK/1/ 答案:https://dev59.com/KGgt5IYBdhLWcg3w8h01#14857326 - mattdlockyer

11

我做了一个jsfiddle示例来演示如何实现:

http://jsfiddle.net/3yHTH/

这个想法是在点击按钮时显示弹出框,在点击按钮外部时隐藏弹出框。

HTML

<a id="button" href="#" class="btn btn-danger">Click for popover</a>

JS

$('#button').popover({
    trigger: 'manual',
    position: 'bottom',
    title: 'Example',
    content: 'Popover example for SO'
}).click(function(evt) {
    evt.stopPropagation();
    $(this).popover('show');
});

$('html').click(function() {
    $('#button').popover('hide');
});

漂亮的演示。我想知道你是如何在jQuery对象上调用弹出框的,弹出框是一个Bootstrap JS插件,但你没有包含任何Bootstrap JS文件? - bingjie2680
在jsfiddle中有一个js文件。看左边的栏目->管理资源。 - Pigueiras
2
我唯一的问题是,当你点击它时,你隐藏了弹出窗口。还不如直接使用工具提示。 - NoBrainer
@NoBrainer 使用matt的解决方案代替我的解决方案吧 =) - Pigueiras
我喜欢这个解决方案,因为它似乎比Matt的解决方案循环使用更少的资源。 - Anonymous
显示剩余2条评论

9
只需在元素中添加此属性。
data-trigger="focus"

最初,我在使用Bootstrap 3.3.7时遇到了问题,但是后来我阅读了文档,发现他们提供了一些值得在这里提及的指导。从文档中的可关闭弹出窗口示例中可以看到:“为了实现适当的跨浏览器和跨平台行为,您必须使用<a>标签而不是<button>标签,并且还必须包括role="button"和tabindex属性。” - Jeff
data-bs-trigger="focus" 在 Bootstrap 5 中的含义是触发焦点。 - undefined

7
只需将此属性添加到HTML元素中,即可在下一次单击时关闭弹出窗口。
data-trigger="focus"

参考自https://getbootstrap.com/docs/3.3/javascript/#dismiss-on-next-click

该功能允许您在下一次单击文档中的任何位置时关闭模态对话框。通过添加 data-dismiss="modal" 属性来实现此功能。例如:

<button type="button" data-dismiss="modal">Close</button>

这实际上是一个非常好的解决方案。我尝试了所有其他答案,除此之外它们都很复杂和脆弱。这个解决方案适用于Bootstrap 4,并且它简单、优雅和健壮。非常感谢! - maesk

6

Bootstrap 5更新:

$(document).on('click', function (e) {
    var
        $popover,
        $target = $(e.target);

    //do nothing if there was a click on popover content
    if ($target.hasClass('popover') || $target.closest('.popover').length) {
        return;
    }

    $('[data-bs-toggle="popover"]').each(function () {
        $popover = $(this);

        if (!$popover.is(e.target) &&
            $popover.has(e.target).length === 0 &&
            $('.popover').has(e.target).length === 0)
        {
            $popover.popover('hide');
        } 
    });
})

它不起作用 :( - Awais
这对我来说是按预期工作的。多次打开/关闭循环是可以的。单击内容不会关闭。单击不同的弹出窗口会关闭第一个,但正确地打开第二个。 - Mir

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