Show one popover and hide other popovers

Twitter Bootstrap

Twitter Bootstrap Problem Overview


i have several buttons and i need a popover for each.
i want it like this:
when my user click on one of them, i want others to be hidden. so only one popover is shown
check and help me correcting this example plz:

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 bellow solved my problem somehow:

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

but by clicking twice on each button it goes wrong

Twitter Bootstrap Solutions


Solution 1 - Twitter Bootstrap

somehow 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 corrected the previous demo i hope it will help someone else

Solution 2 - Twitter Bootstrap

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

Solution 3 - Twitter Bootstrap

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

Solution 4 - Twitter Bootstrap

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;
    }
  });

Solution 5 - Twitter Bootstrap

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.

Solution 6 - Twitter Bootstrap

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

Solution 7 - Twitter Bootstrap

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

Solution 8 - Twitter Bootstrap

This is the simplest and elegant way to do this:

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

Solution 9 - Twitter Bootstrap

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.

Solution 10 - Twitter Bootstrap

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.

Solution 11 - Twitter Bootstrap

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;
         }
    

Solution 12 - Twitter Bootstrap

<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//

Solution 13 - Twitter Bootstrap

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

Solution 14 - Twitter Bootstrap

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.

Solution 15 - Twitter Bootstrap

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();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPersianManView Question on Stackoverflow
Solution 1 - Twitter BootstrapPersianManView Answer on Stackoverflow
Solution 2 - Twitter BootstraplosmescalerosView Answer on Stackoverflow
Solution 3 - Twitter BootstrapAdam HeyView Answer on Stackoverflow
Solution 4 - Twitter BootstrapAndrew BarrettView Answer on Stackoverflow
Solution 5 - Twitter Bootstrapfito dacView Answer on Stackoverflow
Solution 6 - Twitter Bootstrapuser3786660View Answer on Stackoverflow
Solution 7 - Twitter BootstrapTheRealJAGView Answer on Stackoverflow
Solution 8 - Twitter BootstrapMunna KhanView Answer on Stackoverflow
Solution 9 - Twitter BootstrapCoquitoView Answer on Stackoverflow
Solution 10 - Twitter Bootstrapjkofron.eView Answer on Stackoverflow
Solution 11 - Twitter BootstrapPonerosView Answer on Stackoverflow
Solution 12 - Twitter BootstrapdxpkumarView Answer on Stackoverflow
Solution 13 - Twitter BootstrapstathisgView Answer on Stackoverflow
Solution 14 - Twitter BootstrapJacobView Answer on Stackoverflow
Solution 15 - Twitter BootstrapcoolhandchadView Answer on Stackoverflow