How to dismiss a Twitter Bootstrap popover by clicking outside?
Asked Answered
G

41

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. :(

Galloot answered 28/7, 2012 at 17:15 Comment(3)
Similar question : https://mcmap.net/q/101331/-how-can-i-close-a-twitter-bootstrap-popover-with-a-click-from-anywhere-else-on-the-page/1478467Smallminded
Try this https://mcmap.net/q/101332/-detect-click-inside-outside-of-element-with-single-event-handler. Doesn't involve looping through parentsTetramethyldiarsine
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...Barbuto
L
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");
            }
        }
    });
});
Loudspeaker answered 28/7, 2012 at 17:15 Comment(32)
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?Impiety
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'); } });Impiety
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.Loudspeaker
My revision handles open popups now, check it out.Loudspeaker
@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 htmlLoudspeaker
Thank you. Your code actually works with multiple instances of popovers on the same page. Thank you!!!Occiput
I attach to $(document) instead of $('body') since sometimes the body doesn't extend down the entire page.Brittnee
For anyone using angular and bootstrap-ui who is trying to do the same thing, try $('.popover').prev().click(); I had trouble with .hide().Laaspere
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 github.com/twbs/bootstrap/pull/11463Doorstone
Thanks for your comment, hopefully this doesn't f up too many people in the meantime.Loudspeaker
To make it work with Boostrap v3, replace $('.popover-link') by $("[data-toggle='popover']")...Dentalium
@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.Loudspeaker
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.Dentalium
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}.Lumberjack
@Lumberjack what is the issue? Your comment seems out of place.Loudspeaker
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)Lumberjack
Good update, but, it still won't work with popovers that were created from javascript. Check my answer for that solution. https://mcmap.net/q/99219/-how-to-dismiss-a-twitter-bootstrap-popover-by-clicking-outsideBertha
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"Burnish
Instead of '[data-toggle="popover"]', which doesn't work with JavaScript generated popovers, I used '[data-original-title]' as the selector.Ferreira
Way to go! Great solution - separating out the logic and the content.Diplex
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 <li> element too. mail me [email protected] @LoudspeakerUncommitted
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.Phytosociology
Just so everyone knows, the fix for the glitch in the latest version of bootstrap 3.3.5 is here: github.com/twbs/bootstrap/pull/17702Phytosociology
@Phytosociology I'm running BS 3.3.6 and still seeing that behavior where it requires two clicks to open a popoever after dismissing one.Backboard
Workaround for the two clicks bug which still hasn't been fixed: $(this).popover('hide').data('bs.popover').inState.click = falseCornia
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.Hesperidin
@Phytosociology any solution for fixing this 'clicking twice' to enable popover?Trencher
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 ThanksUdella
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.Quaggy
any solution for fixing this 'clicking twice' to enable popover? it's 2021 and it still doesn't workStewartstewed
what about version 5 of bootstrap none of above solution worked for thatZeringue
The edit on Feb 5, 2020 should have never been approved. The updated code is terrible and breaks this workaround.Foskett
A
87
$('html').on('mouseup', function(e) {
    if(!$(e.target).closest('.popover').length) {
        $('.popover').each(function(){
            $(this.previousSibling).popover('hide');
        });
    }
});

This closes all popovers if you click anywhere except on a popover

UPDATE for 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");
        });
    }
});
Aryanize answered 27/3, 2013 at 21:50 Comment(6)
I added a class to the button that triggers the popover (pop-btn) so it isn't included... if(!$(e.target).closest('.popover').length && !$(e.target).closest('.btn').hasClass('pop-btn'))Pawnshop
with 3 popover buttons, this code make problems. in some case I can not click button and buttons are flashed.Epicedium
Can't get this code to work... check this fiddle and please add a fiddle to your answer. jsfiddle.net/C5GBU/102Loudspeaker
Perfect for me. Other answers had side-effects when my "outside click" happened to open a new popover.Allowance
This works great, but needs to have a way to adapt this so that if you click on contents of the popover, it doesn't close. e.g. if you click on text inside a <b> tag inside the popover...Depressor
Supports clicks within child html tags: // closes all popovers if you click anywhere except within a popover $("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"); }); } });Depressor
B
43

Most simple, most fail safe version, works with any bootstrap version.

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

Demo 2: Not dismissing when clicking inside the popover content http://jsfiddle.net/guya/fjZja/

Demo 3: Multiple popovers: http://jsfiddle.net/guya/6YCjW/


Simply calling this line will dismiss all popovers:

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

Dismiss all popovers when clicking outside with this code:

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

The snippet above attach a click event on the body. When the user click on a popover, it'll behave as normal. When the user click on something that is not a popover it'll close all popovers.

It'll also work with popovers that are initiated with Javascript, as opposed to some other examples that will not work. (see the demo)

If you don't want to dismiss when clicking inside the popover content, use this code (see link to 2nd demo):

$('html').on('click', function(e) {
  if (typeof $(e.target).data('original-title') == 'undefined' && !$(e.target).parents().is('.popover.in')) {
    $('[data-original-title]').popover('hide');
  }
});
Bertha answered 28/10, 2013 at 15:1 Comment(8)
Had a similar problem and this worked in Bootstrap 3.Thimblerig
If you bring the popovers closer such that the popovers overlap, when you hide a popover by clicking somewhere outside, one of the links stop working. Check: jsfiddle.net/qjcuyksb/1Carollcarolle
Thanx for the info Sandeep Girl, this was a generic bug in bootstrap 3.0.0 popover. It's fix when updating bootstrap. Check: jsfiddle.net/guya/awu1camLBertha
The last version doesn't work when using bootstrap-datepicker in the popover.Sadducee
I liked this solution the best, because accepted answer started to be a bit of a resource hog with 30 or so popupsEcg
Perhaps !$(e.target).closest('.popover.in').length would be more efficient than !$(e.target).parents().is('.popover.in').Luckey
I found $('[data-original-title]').not(e.target).tooltip('hide'); to be best because you have to dismiss a tooltip/popover when another one is clicked.Cannae
bootstrap 5 updated their data attributes to include "bs" e.g. data-bs-original-titleHarewood
P
22

None of supposed high-voted solutions worked for me correctly. Each leads to a bug when after opening and closing (by clicking on other elements) the popover for the first time, it doesn't open again, until you make two clicks on the triggering link instead of one.

So i modified it slightly:

$(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');
        }
    });
})
Petree answered 27/11, 2015 at 8:43 Comment(6)
Nice, it is working for me. BTW you forgot a ); at the end of your code after last }Husserl
Had the same problem related to the second click. This should be the failsafe answer!Thirty
I also tried the previous solutions above, but as a guidance to those who are also looking for a solution as of 2016, this is a better solution.Komara
top answer, works as advertised. as noted, others did not. this should be the top answerFritzie
Works perfectly except I did not use data-toggel="popover". But you can specify any selector that matches your popover trigger elements. Nice solution and the only one that solved the two click issue for me.Steam
This is prefect!Mcclanahan
M
21

With bootstrap 2.3.2 you can set the trigger to 'focus' and it just works:

$('#el').popover({trigger:'focus'});
Meliorism answered 20/9, 2013 at 6:21 Comment(1)
+1, but important side note: This does not close the popover, if you click again on the button or anchor, while the accepted answer does.Etsukoetta
S
18

This is basically not very complex, but there is some checking to do to avoid glitches.

Demo (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');
});
Smallminded answered 28/7, 2012 at 18:8 Comment(5)
this dismisses the modal by clicking everywhere not outsideMacintyre
Is it possible to do this with a popover() on click rather than hover?Sherbet
Of course, but you need to call stopPropagation() on the event passed to the click handler (if not, the hiding handler immediately hide the popover). Demo (jsfiddle)Smallminded
I've got the same functionality down below in far less code. This answer is bloated and a bit ridiculous for the question... All he wants is to close popovers when you click outside... This is overkill and UGLY!Loudspeaker
Correction, I believe I have BETTER functionality in FAR less code. It assumes you only want one popup at a time to be visible. If you like this please upvote my answer below: jsfiddle.net/P3qRK/1 answer:https://mcmap.net/q/99219/-how-to-dismiss-a-twitter-bootstrap-popover-by-clicking-outsideLoudspeaker
B
11

I made a jsfiddle to show you how to do it:

http://jsfiddle.net/3yHTH/

The idea is to show the popover when you click the button and to hide the popover when you click outside the button.

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');
});
Birdlime answered 28/7, 2012 at 17:40 Comment(7)
nice demo. I am wondering how you could call popover on the Jquery object, popover is a bootstrap js plugin, but you don't include any bootstrap js file there?Catarinacatarrh
There is a js file in the jsfiddle. Look at the left column -> Manage resources.Birdlime
Ok, I see there is a bootstrap js. but it is not checked, it still works?Catarinacatarrh
Yes, it works. Anyway, I searched in google: jsfiddle bootstrap and it gave me the skeleton of bootstrap css+js in jsfiddle.Birdlime
My only problem with this is that you hide the popover when you click it. Might as well just use a tooltip.Teem
@Teem Use matt's solution instead of my solution then =)Birdlime
I like this solution as this seems to use less resource to loop through than matt's solutionForbore
P
9

simply add this attribute with the element

data-trigger="focus"
Perishable answered 6/7, 2017 at 11:49 Comment(2)
Initially this didn't work for me with Bootstrap 3.3.7 but then I read the docs and they have some guidance that's worth a mention here. From the Dismissible popover example in the docs "For proper cross-browser and cross-platform behavior, you must use the <a> tag, not the <button> tag, and you also must include the role="button" and tabindex attributes."Fullgrown
data-bs-trigger="focus" for bootstrap 5Compotation
P
7

Just add this attribute to html element to close popover in next click.

data-trigger="focus"

reference from https://getbootstrap.com/docs/3.3/javascript/#dismiss-on-next-click

Peden answered 7/9, 2017 at 11:19 Comment(1)
This is actually a really great solution. I tried all the other answers and nothing worked, besides they are all complicated and brittle. This solution works for me in Bootstrap 4 and it is simple, elegant and robust. Thanks a lot!Sturdy
C
6

Bootstrap 5 UPDATE:

$(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');
        } 
    });
})
Corpsman answered 17/10, 2021 at 8:4 Comment(2)
its not working :(Zeringue
This is working as expected for me. Multiple open/close cycles are fine. Clicking content doesn't close. Clicking on a different popover closes the first, but correctly opens the second.Casey
D
5

According to http://getbootstrap.com/javascript/#popovers,

<button type="button" class="popover-dismiss" data-toggle="popover" title="Dismissible popover" data-content="Popover Content">Dismissible popover</button>

Use the focus trigger to dismiss popovers on the next click that the user makes.

$('.popover-dismiss').popover({
    trigger: 'focus'
})
Dufour answered 22/7, 2014 at 13:25 Comment(4)
Doesn't work on Mac browsers that follow OS X native behaviour (that doesn't focus nor blur buttons on click). These include Firefox and Safari. Bootstrap guys made a big mistake here, as these popovers can't even be opened, let alone dismissed.Galloot
@AnteVrli Maybe this was not yet in the documentation when you wrote your comment but now the docs say: "For proper cross-browser and cross-platform behavior, you must use the <a> tag, not the <button> tag, and you also must include the role="button" and tabindex attributes." Did you try it with these specifications?Heartwood
Well, there is a problem with this answer, which has nothing to do with platform compatibility: pressing a mouse button inside a popover will dismiss the popover because the element that triggers the popover will lose focus. Forget having users be able to copy-and-paste from popovers: as soon as the mouse button is down, the popover closes. If you have actionable items in the popover (buttons, links), users won't be able to use them.Heartwood
So much for 'cross-platform' because in bootstrap 4.0.0-beta and 4.0.0-beta.2 I cannot get this to work on Mac in Chrome :(Hotien
C
4

This solution works fine :

$("body")   .on('click'     ,'[data-toggle="popover"]', function(e) { 
    e.stopPropagation();
});

$("body")   .on('click'     ,'.popover' , function(e) { 
     e.stopPropagation();
});

$("body")   .on('click'  , function(e) {
        $('[data-toggle="popover"]').popover('hide');
});
Comeback answered 11/5, 2019 at 20:46 Comment(1)
This worked, though only needed the last two of the threeToothy
G
4

For anyone looking for a solution that works with Bootstrap 5 and no jQuery, even when the popovers are dynamically generated (ie manually triggered):

document.querySelector('body').addEventListener('click', function(e) {
    var in_popover = e.target.closest(".popover");

    if (!in_popover) {
        var popovers = document.querySelectorAll('.popover.show');

        if (popovers[0]) {
            var triggler_selector = `[aria-describedby=${popovers[0].id}]`;

            if (!e.target.closest(triggler_selector)) {
                let the_trigger = document.querySelector(triggler_selector);
                if (the_trigger) {
                    bootstrap.Popover.getInstance(the_trigger).hide();
                }
            }
        }
    }
});
Gaynellegayner answered 26/6, 2021 at 7:59 Comment(0)
D
3

This has been asked before here. The same answer I gave then still applies:

I had a similar need, and found this great little extension of the Twitter Bootstrap Popover by Lee Carmichael, called BootstrapX - clickover. He also has some usage examples here. Basically it will change the popover into an interactive component which will close when you click elsewhere on the page, or on a close button within the popover. This will also allow multiple popovers open at once and a bunch of other nice features.

Devereux answered 28/11, 2012 at 11:22 Comment(0)
T
3

This is late to the party... but I thought I'd share it. I love the popover but it has so little built-in functionality. I wrote a bootstrap extension .bubble() that is everything I'd like popover to be. Four ways to dismiss. Click outside, toggle on the link, click the X, and hit escape.

It positions automatically so it never goes off the page.

https://github.com/Itumac/bootstrap-bubble

This is not a gratuitous self promo...I've grabbed other people's code so many times in my life, I wanted to offer my own efforts. Give it a whirl and see if it works for you.

Tiberius answered 14/10, 2013 at 14:47 Comment(0)
M
3

Modified accepted solution. What I've experienced was that after some popovers were hidden, they would have to be clicked twice to show up again. Here's what I did to ensure that popover('hide') wasn't being called on already hidden popovers.

$('body').on('click', function (e) {
    $('[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) {
            var popoverElement = $(this).data('bs.popover').tip();
            var popoverWasVisible = popoverElement.is(':visible');

            if (popoverWasVisible) {
                $(this).popover('hide');
                $(this).click(); // double clicking required to reshow the popover if it was open, so perform one click now
            }
        }
    });
});
Monometallic answered 3/6, 2016 at 16:55 Comment(0)
M
2
jQuery("#menu").click(function(){ return false; });
jQuery(document).one("click", function() { jQuery("#menu").fadeOut(); });
Macintyre answered 20/12, 2012 at 2:34 Comment(0)
P
2

You can also use event bubbling to remove the popup from the DOM. It is a bit dirty, but works fine.

$('body').on('click touchstart', '.popover-close', function(e) {
  return $(this).parents('.popover').remove();
});

In your html add the .popover-close class to the content inside the popover that should close the popover.

Printmaker answered 7/1, 2013 at 21:4 Comment(0)
L
2

It seems the 'hide' method does not work if you create the popover with selector delegation, instead 'destroy' must be used.

I made it work like that:

$('body').popover({
    selector: '[data-toggle="popover"]'
});

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

JSfiddle here

Lambrequin answered 31/10, 2014 at 13:10 Comment(0)
S
2

We found out we had an issue with the solution from @mattdlockyer (thanks for the solution!). When using the selector property for the popover constructor like this...

$(document.body').popover({selector: '[data-toggle=popover]'});

...the proposed solution for BS3 won't work. Instead it creates a second popover instance local to its $(this). Here is our solution to prevent that:

$(document.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) {
            var bsPopover = $(this).data('bs.popover'); // Here's where the magic happens
            if (bsPopover) bsPopover.hide();
        }
    });
});

As mentioned the $(this).popover('hide'); will create a second instance due to the delegated listener. The solution provided only hides popovers which are already instanciated.

I hope I could save you guys some time.

Sulfite answered 2/3, 2015 at 17:48 Comment(0)
E
2

Bootstrap natively supports this:

JS Bin Demo

Specific markup required for dismiss-on-next-click

For proper cross-browser and cross-platform behavior, you must use the <a> tag, not the <button> tag, and you also must include the role="button" and tabindex attributes.

Exterminatory answered 5/11, 2016 at 4:13 Comment(2)
In bootstrap 4.0.0-beta and 4.0.0-beta.2 I cannot get this to work on Mac in Chrome :(Hotien
Clicking anywhere closes this popover, the questioner requires "click outside the popover closes it", which is different.Bierce
S
2

this solution gets rid of the pesky 2nd click when showing the popover for the second time

tested with with Bootstrap v3.3.7

$('body').on('click', function (e) {
    $('.popover').each(function () {
        var popover = $(this).data('bs.popover');
        if (!popover.$element.is(e.target)) {
            popover.inState.click = false;
            popover.hide();                
        }
    });
});
Socialistic answered 19/12, 2016 at 20:21 Comment(0)
A
2

I've tried many of the previous answers, really nothing works for me but this solution did:

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

They recommend to use anchor tag not button and take care of role="button" + data-trigger="focus" + tabindex="0" attributes.

Ex:

<a tabindex="0" class="btn btn-lg btn-danger" role="button" data-toggle="popover" 
data-trigger="focus" title="Dismissible popover" data-content="amazing content">
Dismissible popover</a>
Archeology answered 20/2, 2018 at 8:31 Comment(1)
try this reference too : #20467403Archeology
V
1

I came up with this: My scenario included more popovers on the same page, and hiding them just made them invisible and because of that, clicking on items behind the popover was not possible. The idea is to mark the specific popover-link as 'active' and then you can simply 'toggle' the active popover. Doing so will close the popover completely $('.popover-link').popover({ html : true, container: 'body' })

$('.popover-link').popover().on 'shown.bs.popover', ->
  $(this).addClass('toggled')

$('.popover-link').popover().on 'hidden.bs.popover', ->
  $(this).removeClass('toggled')

$("body").on "click", (e) ->
  $openedPopoverLink = $(".popover-link.toggled")
  if $openedPopoverLink.has(e.target).length == 0
    $openedPopoverLink.popover "toggle"
    $openedPopoverLink.removeClass "toggled"
Vitalis answered 29/10, 2013 at 20:25 Comment(0)
V
1

I just remove other active popovers before the new popover is shown (bootstrap 3):

$(".my-popover").popover();

$(".my-popover").on('show.bs.popover',function () {
    $('.popover.in').remove();
});              
Vedis answered 17/1, 2014 at 14:48 Comment(0)
S
1

The answer from @guya works, unless you have something like a datepicker or timepicker in the popover. To fix that, this is what I have done.

if (typeof $(e.target).data('original-title') === 'undefined' && 
    !$(e.target).parents().is('.popover.in')) {
        var x = $(this).parents().context;
        if(!$(x).hasClass("datepicker") && !$(x).hasClass("ui-timepicker-wrapper")){
            $('[data-original-title]').popover('hide');
        }
}
Sadducee answered 4/3, 2015 at 14:40 Comment(0)
V
1

tested with 3.3.6 and second click is ok

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

        $(document).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');
                }
            });
        });
Vaccinia answered 25/7, 2016 at 17:4 Comment(0)
H
0

demo: http://jsfiddle.net/nessajtr/yxpM5/1/

var clickOver = clickOver || {};
clickOver.uniqueId = $.now();

clickOver.ClickOver = function (selector, options) {
    var self = this;

    //default values
    var isVisible, clickedAway = false;

    var callbackMethod = options.content;
var uniqueDiv = document.createElement("div");
var divId = uniqueDiv.id = ++clickOver.uniqueId;
uniqueDiv.innerHTML = options.loadingContent();

options.trigger = 'manual';
options.animation = false;
options.content = uniqueDiv;

self.onClose = function () {
    $("#" + divId).html(options.loadingContent());
    $(selector).popover('hide')
    isVisible = clickedAway = false;
};
self.onCallback = function (result) {
    $("#" + divId).html(result);
};

$(selector).popover(options);

//events
$(selector).bind("click", function (e) {
    $(selector).filter(function (f) {
        return $(selector)[f] != e.target;
    }).popover('hide');

    $(selector).popover("show");
    callbackMethod(self.onCallback);

    isVisible = !(clickedAway = false);
});

$(document).bind("click", function (e) {
    if (isVisible && clickedAway && $(e.target).parents(".popover").length == 0) {
        self.onClose();
        isVisible = clickedAway = false;
    } else clickedAway = true;
});

}

this is my solution for it.

Harass answered 4/3, 2013 at 8:20 Comment(0)
C
0

This approach ensures that you can close a popover by clicking anywhere on the page. If you click on another clickable entity, it hides all other popovers. The animation:false is required else you will get a jquery .remove error in your console.

$('.clickable').popover({
 trigger: 'manual',
 animation: false
 }).click (evt) ->
  $('.clickable').popover('hide')
  evt.stopPropagation()
  $(this).popover('show')

$('html').on 'click', (evt) ->
  $('.clickable').popover('hide')
Corney answered 5/8, 2013 at 8:40 Comment(0)
D
0

Ok this is my first attempt at actually answering something on stackoverflow so here goes nothing :P

It appears that it isn't quite clear that this functionality actually works out of the box on the latest bootstrap (well, if you're willing to compromise where the user can click. I'm not sure if you have to put 'click hover' per-se but on an iPad, click works as a toggle.

The end result is, on a desktop you can hover or click (most users will hover). On a touch device, touching the element will bring it up, and touching it again will take it down. Of course, this is a slight compromise from your original requirement but at least your code is now cleaner :)

$(".my-popover").popover({ trigger: 'click hover' });

Dinadinah answered 16/8, 2013 at 4:3 Comment(0)
C
0

Taking Matt Lockyer's code, I've done a simple reset so the dom doesn't get covered by the element on hide.

Matt's code: http://mattlockyer.com/2013/04/08/close-a-twitter-bootstrap-popover-when-clicking-outside/

Fiddle: http://jsfiddle.net/mrsmith/Wd2qS/

    $('body').on('click', function (e) {
    //hide popover from dom to prevent covering elements
    $('.popover').css('display', 'none');
    //bring popover back if trigger element is clicked
    $('[data-toggle="popover"]').each(function () {
        if ($(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
            $('.popover').css('display', 'block');
        }
    });
    //hide popover with .popover method
    $('[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');
        }
    });
});
Conformal answered 1/5, 2014 at 13:44 Comment(0)
S
0

Try this, this will hide by clicking outside.

$('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');
    }
    });
});
Skeens answered 21/7, 2014 at 12:55 Comment(0)
W
0

I was having issues with mattdlockyer's solution because I was setting up popover links dynamically using code like this:

$('body').popover({
        selector : '[rel="popover"]'
});

So I had to modify it like so. It fixed a lot of issues for me:

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

Remember that destroy gets rid of the element, so the selector part is important on initializing the popovers.

Wentworth answered 8/9, 2014 at 21:19 Comment(0)
C
0
$('html').on('click.popover', function (e) {
    var allpopins = $('.popover.in');
    if (allpopins.has(e.target).length > 0 &&
        !$('.btn', allpopins).is(e.target))
        return;
    // recognise pop-up 
    var id = $(e.target).attr('aria-describedby');
    var popin = $("#" + id);
    //on any button click in entire pop-up hide this pop-ups
    $(popin).on(".btn", function () { popin.remove(); });
    // on any place out of entire popup hide all pop-ups 
    $('.popover.in').not(popin).remove();
});

This is my the best performance solution. Cheers.

Cluster answered 15/5, 2015 at 6:33 Comment(0)
H
0

The answer works very well, just to add a angular directive in case you are using angular like me:

app.directive('popover', ['$document', function($document) {
    return {
        restrict: 'EA',
        link: function(scope, elem, attrs) {
            $(document).ready(function() {
                $('[data-toggle="popover"]').popover();
            });

            elem.bind('click', function(e) {
                $('#notification').popover('toggle');
            })

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

The html code:

<a popover tabindex="0" role="button"
   id="notification"
   data-toggle="popover" data-trigger="manual"
   data-container="body" data-placement="bottom"
   data-content="This is a popover">
   Popover button
</a>

It should have been as simple as use data-trigger='click focus', because according to bootstrap:

How popover is triggered - click | hover | focus | manual. You may pass multiple triggers; separate them with a space. manual cannot be combined with any other trigger.

However, use click and focus together doesn't work for me for unknown reason, instead I have to toggle it manually.

Hindenburg answered 21/6, 2016 at 14:42 Comment(0)
B
0
$(document).on('click', function(e) {
  $('[data-toggle="popover"]').each(function() {
    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
    }

  });
});
Bunsen answered 6/1, 2017 at 5:34 Comment(1)
Please add more information/description on how this work and solve the question, so that others can understand it easily without asking for more detail. Thank you :)Ta
S
0

There's an issue with the latest update of the accepted answer: If you have some tooltips enabled and you open a popover and then click on a tooltip-having element, the text of the tooltip will be displayed as a popover to.

To prevent this, just add

 if ($(this).data('bs.popover')) {

inside the if-statement (or add it concatenated with && to the if-statement)

// only show one popover at the time and hide on clicking outside
$(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).data('bs.popover')) {
                (($(this).popover('hide').data('bs.popover') || {}).inState || {}).click = false  // fix for BS 3.3.6
            }
        }
    });
});
Stearic answered 18/4, 2017 at 7:10 Comment(0)
B
0

I found a good and simple solution to this problem. Doing it this way, we only have an event listener activated when it needs to be. Furthermore we don't have a problem with the popover not appearing because an event disarming the popup is firing at the same time.

Add the popover to the HTML

   <input id="popoverId" type="text" data-toggle="popover" data-trigger="manual" data-content="Popover content">

Show the popover when you want.

$('#popoverId').popover('show');

Add this listener to your Javascript. This listener will fire as soon as the popover is shown and will attach a listener to the body which will hide the popover when the user clicks anywhere on the body of the page and then remove the event listener until the popover will be shown again.

$('#popoverId').on('shown.bs.popover', function () {
    $('body').click(function () {
        $('#popoverId').popover('hide');
        $('body').off();
    });
});
Benedetto answered 29/3, 2019 at 9:50 Comment(0)
C
0

According to highest two answers, I have a little fix:

<span class="btn btn-info btn-minier popover-info" data-rel="popover"
                                              data-placement="bottom" data-html="true" title=""
                                              data-content="popover-content"
                                              data-original-title="popover-title">
                                            <i class="ace-icon fa fa-info smaller-100"></i>
                                        </span>
            $('[data-rel=popover]').popover({html: true});
            $(document).on("shown.bs.popover", '[data-rel=popover]', function () {
                $('[data-rel="popover"][popover-show="1"]').popover('hide');
                $(this).attr('popover-show', '1');
            });
            $(document).on("hidden.bs.popover", '[data-rel=popover]', function () {
                if ($(this).attr('popover-show') === '0') {
                    // My important fix: using bootstrap 3.4.1, if hide popover by .popover('hide') and click to show, popover internal treat it is already shown and dispatch hidden event immediately without popover anything.
                    $(this).popover('toggle');
                } else {
                    $(this).attr('popover-show', '0');
                }
            });
            $('html').on('click', function (e) {
                if (typeof $(e.target).data('original-title') == 'undefined'
                    && typeof $(e.target).parent().data('original-title') == 'undefined'
                    && !$(e.target).parents().is('.popover.in')) {
                    $('[data-rel="popover"][popover-show="1"]').popover('hide');
                }
            });
Contreras answered 19/10, 2020 at 4:36 Comment(0)
H
0

This worked for me.

Breakdown:

First, add a click event listener to the body.

On Click, check whether or not there's an element with a popover class (Bootstrap removes the popover element from the DOM on dismissal).

If the .popover element exists, check the event.target which will tell you where the User clicked on the page. If it's part of the .popover element, do nothing. If not, hide the popover (docs here).

NOTE: The aria-describedby condition prevents the popover from being hidden when it is initially fired/shown.

document.body.addEventListener('click', event => {
  let isPopoverShown = Boolean(document.querySelector('.popover'));
  let isClickNotPopover = !event.target.closest('.popover');

  if (
        isPopoverShown 
     && isClickNotPopover 
     && !event.target?.getAttribute('aria-describedby')?.startsWith('popover')
  ) {
     $('.popover').popover('hide')
  }

}) 
Hoggard answered 30/6, 2022 at 20:27 Comment(0)
C
-2
$("body").find('.popover').removeClass('in');
Cottar answered 27/7, 2016 at 14:30 Comment(1)
Can you please edit your answer add some commentary to this line? What it does, how it works, how it differs from other solutions, whether there are any caveats... Thank you!Mirabel

© 2022 - 2024 — McMap. All rights reserved.