PersianMan
PersianMan

Reputation: 2779

Show one popover and hide other popovers

I have several buttons and I need a popover for each. When my user clicks on one of them, I want the others to be hidden, so that only one popover is shown. Please help me correct this example:

var mycontent='<div class="btn-group"> <button class="btn">Left</button> <button class="btn">Middle</button> <button class="btn">Right</button> </div>'
 
$('.btn').popover({
    html: true,
    content:mycontent,
    trigger: 'manual'
}).click(function(e) {
    $(this).popover('toggle');
    e.stopPropagation();
});

$('html').click(function(e) {
     $('.btn').popover('hide');
});

my html:

<ul>
    <li>
        <a href="#" class="btn" data-toggle="popover" data-placement="bottom" title="" >Popover</a>
    </li>
    <li>
       <a href="#" class="btn" data-toggle="popover" data-placement="bottom" title="" >Popover</a> 
    </li>
</ul>

jsfiddle example

Adding something like the code below solved my problem somehow:

$('.btn').click(function(e) {
     $('.btn').popover('hide');
});

but after clicking twice on each button, it goes wrong!

Upvotes: 72

Views: 77155

Answers (15)

PersianMan
PersianMan

Reputation: 2779

I created one example for my need. I used this code:

$('.btn').popover();

$('.btn').on('click', function (e) {
    $('.btn').not(this).popover('hide');
});

check the demo here

... and I corrected the previous demo.

Upvotes: 160

coolhandchad
coolhandchad

Reputation: 1

This works perfectly. I had the same problem. This is the best solution I worked out for it. Add this code in place of Bootstrap's popover initialization code.

function popoverInit() {
    // Get all of the popover links/btns
    let popoverList = document.querySelectorAll("[data-bs-toggle='popover']");
    // Loop through each popover link/btn
    popoverList.forEach(popoverItem => {
        // Initiate bootstrap.popover() and set it to be triggered manually
        let popover = new bootstrap.Popover(popoverItem, {
            trigger: "manual"
        });
        // Add click event to popover links/btns
        popoverItem.addEventListener("click", () => {
            // Once one popover has been clicked open, start capturing their "content wrapper divs" into an array (there aren't any until one has been clicked)
            let popoverWrappers = document.querySelectorAll(".popover.show");
            let wrappersArr = Array.from(popoverWrappers);
            popover.show(); // On first popover clicked, show it
            // Loop through each popover body
            wrappersArr.forEach(wrapper => {
                let wrapperToDeleteId = wrapper.getAttribute("id"); // Get the ID of the previously opened popover body
                // If the item's id matches, then remove it from the DOM, else toggle the current one open
                if ((wrapper.id = wrapperToDeleteId)) {
                    wrapper.remove();
                } else {
                    popover.toggle();
                }
            });
        });
    });
}

popoverInit();

Upvotes: 0

Jacob
Jacob

Reputation: 7821

As a few comments point out, calling popover('hide') to hide any displayed popovers results in having to click twice to get the popover back if you want to see it again.

I think this is caused by a bug introduced sometime after v3.2 with the introduction of the inState.click property.

Tooltip.prototype.toggle modifies this property in v3.4 for example. (Source)

Tooltip.prototype.hide/show should also modify it but does not. (Source)

So if you call popover('hide') the inState.click will remain true. Then when you click on the trigger element, it will call Tooltip.prototype.toggle which still thinks the popover is displayed and negates the click property because it's trying to hide it. Another click is then required to show it.

I see 2 solutions here. Use toggle rather than hide/show or fix the bug:

A one line fix seems to correct this issue (at least in v3.4):

  Tooltip.prototype.hide = function (callback) {
    this.inState.click = false; //add this line

This is based on my limited understanding of popover/tooltip functionality. Hopefully it doesn't introduce other bugs.

Upvotes: 0

stathisg
stathisg

Reputation: 13

When an icon is clicked and has open the corresponding popover then it has a value that begins with "popover*" called aria-describedby.

So you find this icons and trigger click on them but not on the icon which is clicked now.

$('.icon-info').click(function(){
    $(".icon-info[aria-describedby*='popover']").not(this).trigger('click');
});

Upvotes: 0

Munna Khan
Munna Khan

Reputation: 2052

This is the simplest and elegant way to do this:

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

Upvotes: 5

Coquito
Coquito

Reputation: 41

With the help of "losmescaleros" answer, this works perfectly for me :

$('body').popover({
  selector: '[data-toggle="popover"]',
  trigger: "click"
}).on("show.bs.popover", function(e){
  // hide all other popovers
  $("[data-toggle='popover']").not(e.target).popover("destroy");                  
});

Without any double click issues.

Upvotes: 3

dxpkumar
dxpkumar

Reputation: 371

<ul>
<li><i class="fa fa-trash-o DeleteRow" id=""></i>1</li>
<li><i class="fa fa-trash-o DeleteRow" id=""></i>2</li>
<li><i class="fa fa-trash-o DeleteRow" id=""></i>3</li>
</ul>

// Close other popover when click on Delete/Open new popover - START //
     $('.DeleteRow').on('click', function (e) {
        $('.popover').not(this).popover('hide');
     });
 // Close other popover when click on Delete/Open new popover - END//

Upvotes: 0

TheRealJAG
TheRealJAG

Reputation: 2098

Here's a solution that worked for me. In my scripts I don't pass vars through the data attribute in the HTML, I prefer the logic in my js files.

            $(".vote").popover({
                trigger: " click",
                title: "Attention",
                content: "You must be a member of the site to vote on answers.",
                placement: 'right'
            });

            $('.vote').on('click', function (e) {
                $('.vote').not(this).popover('hide');
            });

Upvotes: 4

fito dac
fito dac

Reputation: 51

Using Bootstrap 3.3.7 I find this solution:

var _popoverLink = $('[data-toggle="popover"]');

_popoverLink.on('click', function(){
  _popoverLink.popover('destroy').popover({container: 'body'});
  $(this).popover('show');
});

regards.

Upvotes: 5

Poneros
Poneros

Reputation: 39

I went for a combinations of approaches I've seen both on this thread and others. My requirements specify that:

  • Only one popover should be visible at a time
  • A click anywhere outside the popover should dismiss the popover
  • The popover should contain a control
  • Should not require rebinding the popover event

        function bindEvents() {
    
             setupPopupBinding();
    
             setupPopupDismissal();
        };
    
        function setupPopupBinding() {
               $('.addSectionItem').popover({
                      html: true,
                      content: function () {
                            return createDropdowns($(this).data('sectionid'))[0].outerHTML;
                      },
                      placement: "right",
                      trigger: "click focus"
               }).on("inserted.bs.popover", function (e) {
                    //initialize dropdown
                    setupSelect2();
               }).on("hide.bs.popover", function (e) {
                     $('.select2-container--open').remove();
               });
        }
    
        function setupPopupDismissal() {
               $('body:not(.addSectionItem)').on('click', function (e) {
               $('.addSectionItem').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');
               $('.popover').has(e.target).remove();
            }
        });
        });
        }
    
        function createDropdowns(sectionId: number) {
               var dropdown = $('<div/>')
               .attr('Id', 'sectionPopupWrapper' + sectionId)
               .addClass('popupWrapper')
               .append($('<select/>').addClass('sectionPopup'))
               .append($('<button/>').addClass('btn btn-primary btn-xs')
               .attr('data-sectionid', sectionId)
               .text('Add'));
    
        return dropdown;
        }
    

Upvotes: 0

user3786660
user3786660

Reputation: 41

You are taking this too seriously, just close every opened popover before triggering the new one to be opened:

// Hide any active popover first
            $(".popover").each(function () {
                var $this = $(this);
                $this.popover('hide');
            });

//Now Execute your new popover
$('.btn').popover({
                html: true,
                content: mycontent,
                trigger: 'manual'
            }).click(function (e) {
                $(this).popover('toggle');
                e.stopPropagation();
            });

Upvotes: 4

gnomical
gnomical

Reputation: 5043

I was having some difficulty using any of the answers posted to solve this issue using bootstrap v3. After some searching I found my primary issue was not setting the proper popover trigger. It should be set as 'manual' as listed in the op's question.

The next step was very simple and gives some better behavior than the solutions I see in the other answers so I thought I would share.

$('html').on('click', function(e) {
  if($(e.target).hasClass('btn')) {
    $(e.target).popover('toggle');
  }

  if(!$(e.target).parent().hasClass('popover')) {
    $('.popover').prev('.btn').not(e.target).popover('toggle');
  }
});

This solution gives you the ability to dismiss the popover upon clicking anywhere else on the page including another popover link but allows you to click on the popover itself without dismissing it so that the popover can be accessed by the user for things like copy pasting text.

Hope this helps someone, here is a working fiddle. https://jsfiddle.net/hL0pvaty/

p.s. - I am only using the .btn class as the selector in my example because it is used in the op's question. I would recommend using something less generic.

Upvotes: 0

Adam Hey
Adam Hey

Reputation: 1691

The easiest way to do this is to set trigger="focus" in your popover

Dismiss on next click

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

<a tabindex="0" class="btn btn-lg btn-danger" role="button" data-toggle="popover" data-trigger="focus" title="Dismissible popover" data-content="And here's some amazing content. It's very engaging. Right?">Dismissible popover</a>

Note - this will mean the popover will hide as soon as you click off of it

Upvotes: 30

losmescaleros
losmescaleros

Reputation: 498

None of the answers I saw previously had dynamic popovers, so this is what I came up with. As some have pointed out, there are issues with popovers causing problems if they aren't removed from the DOM using .remove(). I forked an example from the bootstrap website and created this new fiddle. Dynamic popovers are added using the selector: '[rel=popover]' option. When a popover is about to be shown, I call destroy on all the other popovers, then remove the .popover content from the page.

$('body').popover({
                selector: '[rel=popover]',
                trigger: "click"
            }).on("show.bs.popover", function(e){
                // hide all other popovers
                $("[rel=popover]").not(e.target).popover("destroy");
                $(".popover").remove();                    
            });

Upvotes: 30

Andrew Barrett
Andrew Barrett

Reputation: 19911

This is a quick generic solution that I'm using where you don't need to know what the classes are of the popovers in advance. I haven't tested it super extensively. Also I'm using toggle below as I had some problems with the hide behaving quite differently than the toggle.

  var $currentPopover = null;
  $(document).on('shown.bs.popover', function (ev) {
    var $target = $(ev.target);
    if ($currentPopover && ($currentPopover.get(0) != $target.get(0))) {
      $currentPopover.popover('toggle');
    }
    $currentPopover = $target;
  });

  $(document).on('hidden.bs.popover', function (ev) {
    var $target = $(ev.target);
    if ($currentPopover && ($currentPopover.get(0) == $target.get(0))) {
      $currentPopover = null;
    }
  });

Upvotes: 10

Related Questions