Reputation: 2779
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>
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
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
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
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
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
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
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
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
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
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
Reputation: 39
I went for a combinations of approaches I've seen both on this thread and others. My requirements specify that:
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
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
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
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
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
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