311

Can we get popovers to be dismissable in the same way as modals, ie. make them close when user clicks somewhere outside of them?

Unfortunately I can't just use real modal instead of popover, because modal means position:fixed and that would be no popover anymore. :(

ahsteele
  • 26,243
  • 28
  • 134
  • 248
Ante Vrli
  • 3,175
  • 2
  • 17
  • 13
  • 3
    Similar question : http://stackoverflow.com/q/8947749/1478467 – Sherbrow Dec 20 '12 at 19:33
  • Try this https://stackoverflow.com/a/40661543/5823517. Doesn't involve looping through parents – Tunn Aug 20 '18 at 04:27
  • `data-trigger="hover"` and `data-trigger="focus"` are built-in alternative for closing the popover, if you don't want to use toggle. In my opinion, `data-trigger="hover"` provides the best user experience... there is no need to write extra .js code... – Hooman Bahreini Apr 24 '19 at 07:20

41 Answers41

482

Update: A slightly more robust solution: http://jsfiddle.net/mattdlockyer/C5GBU/72/

For buttons containing text only:

$('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');
    }
});

For buttons containing icons use (this code has a bug in Bootstrap 3.3.6, see the fix below in this answer)

$('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');
        }
    });

For JS Generated Popovers Use '[data-original-title]' in place of '[data-toggle="popover"]'

Caveat: The solution above allows multiple popovers to be open at once.

One popover at a time please:

Update: Bootstrap 3.0.x, see code or fiddle 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');
        }
    });
});

This handles closing of popovers already open and not clicked on or their links have not been clicked.


Update: Bootstrap 3.3.6, see fiddle

Fixes issue where after closing, takes 2 clicks to re-open

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

    });
});

Update: Using the conditional of the previous improvement, this solution was achieved. Fix the problem of double click and ghost popover:

$(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");
            }
        }
    });
});
SiranCole
  • 3
  • 2
mattdlockyer
  • 6,984
  • 4
  • 40
  • 44
  • Well, consider 2 buttons next to each other. Each has the class popover-link so if you click both buttons, both popovers appear and overlap each other. However, if i click the second button, i want the first popover to disapear. but it wont, because both buttons are catchedbecause of 'popover-link. Is there some way to close each popover that dos not belong to the clicked popover-link or popover? – Alex Schneider Feb 14 '13 at 14:46
  • 1
    by the way, i extented your function: $('body').click(function (e) { if ($('.popover, .popover-trigger').has(e.target).length === 0 && !$('.popover, .popover-trigger').is(e.target)) { $('.popover-trigger').popover('hide'); } }); – Alex Schneider Feb 14 '13 at 14:48
  • Cool man thanks, yea I wasn't too concerned about all the popovers being open because the last opened popover will always have the higher z index and you can just click outside to close them all... But I like your solution. – mattdlockyer Feb 17 '13 at 02:51
  • 1
    My revision handles open popups now, check it out. – mattdlockyer Feb 21 '13 at 16:28
  • @AlexSchneider thanks for your input, the is(e.target) was used here. The reason I only use the has(e.target) and length is because I always have icons inside a popover target container might revise my html – mattdlockyer Feb 21 '13 at 17:56
  • Thank you. Your code actually works with multiple instances of popovers on the same page. Thank you!!! – Tabetha Moe May 27 '13 at 02:30
  • 2
    I attach to `$(document)` instead of `$('body')` since sometimes the `body` doesn't extend down the entire page. – jasop Jul 05 '13 at 15:10
  • For anyone using angular and bootstrap-ui who is trying to do the same thing, try $('.popover').prev().click(); I had trouble with .hide(). – aet Aug 22 '13 at 23:38
  • There is a bug in bootstrap v3.0.2 so `.popover('hide')` does not work anymore. This should be fixed in upcoming version v3.0.3 https://github.com/twbs/bootstrap/pull/11463 – mkurz Nov 20 '13 at 09:52
  • Thanks for your comment, hopefully this doesn't f up too many people in the meantime. – mattdlockyer Nov 21 '13 at 16:51
  • To make it work with Boostrap v3, replace $('.popover-link') by $("[data-toggle='popover']")... – ValentinH Nov 24 '13 at 14:43
  • @46enforce is that not just changing the selector? Would you not end up with the same object? My example implies that you add the class 'popover-link' to your popover links... But nice to see a solution that doesn't require this. Thanks. – mattdlockyer Nov 26 '13 at 20:33
  • OK I though that there was the .popover-link in Bootstrap 2 that's why. :) Indeed, my selector does the same thing than yours but it releases the need of using an extra class. – ValentinH Nov 27 '13 at 12:53
  • 2
    So the hidden-but-still-active-popover issue remains. Could be fixed with a little css, though: .popover.fade {z-index:-1} .popover.fade.in {z-index:99}. – ravb79 Jan 22 '14 at 09:46
  • @ravb79 what is the issue? Your comment seems out of place. – mattdlockyer Jan 22 '14 at 16:42
  • 6
    After activating the popover (and the subsequent hide action), the popover isn't completely hidden; it's just not visible. The issue is that content underneath the invisible yet present popover can't be clicked or hovered. Issue occurs on latest Chrome build, latest bootstrap 3 .js (could be other browsers as well, couldn't be bothered to check since this workaround should be required anyway) – ravb79 Jan 22 '14 at 20:47
  • Good update, but, it still won't work with popovers that were created from javascript. Check my answer for that solution. http://stackoverflow.com/a/19638199/275333 – guya Jan 26 '14 at 20:16
  • Just a comment to say it also works with bootstrap v2.3. I am talking about the last example: "One popover at a time please" – Paco Aug 26 '14 at 10:47
  • 6
    Instead of `'[data-toggle="popover"]'`, which doesn't work with JavaScript generated popovers, I used `'[data-original-title]'` as the selector. – Nathan Sep 30 '14 at 17:01
  • 1
    Way to go! Great solution - separating out the logic and the content. – Ryan Walton Oct 07 '14 at 22:01
  • How to make the element exception? My popover can be closed by clicking anywhere outside the box. But, **I want the popover doesn't close when I click on the `
  • ` element too**. mail me okierie@gmail.com @mattdlockyer
  • – Oki Erie Rinaldi Dec 24 '14 at 06:43
  • 4
    Does anyone know why this solution does not work with the latest version of bootstrap? The following is happening: Click button to show popover, then click body to dismiss popover, then click button to show popover and popover does not show. After it fails once if you click it again it shows. It is very strange. – JTunney Oct 22 '15 at 15:39
  • 1
    Just so everyone knows, the fix for the glitch in the latest version of bootstrap 3.3.5 is here: https://github.com/twbs/bootstrap/pull/17702 – JTunney Oct 22 '15 at 16:10
  • 3
    @JTunney I'm running BS 3.3.6 and still seeing that behavior where it requires two clicks to open a popoever after dismissing one. – sersun Apr 22 '16 at 18:09
  • 2
    Workaround for the two clicks bug which still hasn't been fixed: `$(this).popover('hide').data('bs.popover').inState.click = false` – Gannet Jul 17 '16 at 21:40
  • 3
    Unbelievable, I implemented your solution years ago but it doesn't work after updating. I cant believe I'm seeing your post again with updated solutions from time to time. I wish I could double up your answer. Looking forward to check out your solution when V4 is out. – CalebC Nov 10 '16 at 11:37
  • @JTunney any solution for fixing this 'clicking twice' to enable popover? – Santosh Nov 23 '16 at 06:28
  • The solution that works for me is the "Update: Bootstrap 3.3.6" (no double click outside needed, single popover at a time) I'm, Using bootstrap 3.3.5 Thanks – Ignacio Vazquez Apr 18 '17 at 14:54
  • In case of issue with `display: none` of popover (bootstrap 3.* issue on hide method), you need to add: `$(this).parent().find('.popover').remove();` after the `hide` call. – thejoin Oct 16 '17 at 10:19
  • any solution for fixing this 'clicking twice' to enable popover? it's 2021 and it still doesn't work – Cristiano Felipe Jan 15 '21 at 19:03
  • what about version 5 of bootstrap none of above solution worked for that – Awais Jul 05 '22 at 12:35
  • The edit on Feb 5, 2020 should have never been approved. The updated code is terrible and breaks this workaround. – Rudey Aug 26 '22 at 10:29