62

I'm using Twitter Bootstrap's popover on the dynamic list. The list item has a button, when I click the button, it should show up popover. It works fine when I tested on non-dynamic.

this is my JavaScript for non-dynamic list

$("button[rel=popover]").popover({ 
    placement : 'right',
    container : 'body',
    html : true,
    //content:" <div style='color:red'>This is your div content</div>"
    content: function() {
      return $('#popover-content').html();
    }

    })
    .click(function(e) {
        e.preventDefault();
});

However, It doesn't work well on dynamic list. It can show up when I click the button "twice" and only show up one of list items I click fist time.

MY html:

 <ul id="project-list" class="nav nav-list">
   <li class='project-name'>
     <a >project name 1
         <button class="pop-function" rel="popover" ></button>
     </a>
   </li>
   <li class='project-name'>
     <a>project name 2
        <button class="pop-function" rel="popover" ></button>
     </a>
   </li>

 </ul>

<div id="popover-content" style="display:none">
    <button class="pop-sync"></button>
    <button class="pop-delete"></button>
</div>

My JavaScript for dynamic:

$(document).on("click", "#project-list li" , function(){
   var username = $.cookie("username");
   var projectName = $(this).text()
   $("li.active").removeClass("active");
   $(this).addClass("active");
   console.log("username: " +username + " project name: "+projectName );
});


$(document).on("click", "button[rel=popover]", function(){
    $(this).popover({ 
       placement : 'right',
       container : 'body',
       html : true,
    content: function() {
       return $('#popover-content').html();
        }

    }).click(function(e){
    e.preventDefault();
    })

});


//for close other popover when one popover button click
$(document).on("click", "button[rel=popover]" , function(){

        $("button[rel=popover]").not(this).popover('hide');
 });

I have searched similar problems, but I still can't find the one to solve my problem. If anyone has some ideas, please let me know. Thanks your help.

mplungjan
  • 169,008
  • 28
  • 173
  • 236
user2150267
  • 673
  • 1
  • 7
  • 6
  • Dupe. The answer here is simpler and worked just fine http://stackoverflow.com/questions/18731094/adding-different-twitter-bootstrap-popovers-to-dynamically-created-elements – giorgio79 Aug 03 '16 at 10:15

6 Answers6

104

Update

If your popover is going to have a selector that is consistent then you can make use of selector property of popover constructor.

var popOverSettings = {
    placement: 'bottom',
    container: 'body',
    html: true,
    selector: '[rel="popover"]', //Sepcify the selector here
    content: function () {
        return $('#popover-content').html();
    }
}

$('body').popover(popOverSettings);

Demo

Other ways:

  1. (Standard Way) Bind the popover again to the new items being inserted. Save the popoversettings in an external variable.
  2. Use Mutation Event/Mutation Observer to identify if a particular element has been inserted on to the ul or an element.

Source

var popOverSettings = { //Save the setting for later use as well
    placement: 'bottom',
    container: 'body',
    html: true,
    //content:" <div style='color:red'>This is your div content</div>"
    content: function () {
        return $('#popover-content').html();
    }

}

$('ul').on('DOMNodeInserted', function () { //listed for new items inserted onto ul
    $(event.target).popover(popOverSettings);
});

$("button[rel=popover]").popover(popOverSettings);
$('.pop-Add').click(function () {
    $('ul').append("<li class='project-name'>     <a>project name 2        <button class='pop-function' rel='popover'></button>     </a>   </li>");
});

But it is not recommended to use DOMNodeInserted Mutation Event for performance issues as well as support. This has been deprecated as well. So your best bet would be to save the setting and bind after you update with new element.

Demo

Another recommended way is to use MutationObserver instead of MutationEvent according to MDN, but again support in some browsers are unknown and performance a concern.

MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
// create an observer instance
var observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
        $(mutation.addedNodes).popover(popOverSettings);
    });
});

// configuration of the observer:
var config = {
     attributes: true, 
     childList: true, 
     characterData: true
};

// pass in the target node, as well as the observer options
observer.observe($('ul')[0], config);

Demo

Community
  • 1
  • 1
PSL
  • 123,204
  • 21
  • 253
  • 243
  • Hi PSL Thanks for your help. I really don't understand MutationObserver. I tired to use your code on demo. It works fine when I click on dynamic list, but the problem is the popover should be show up when click the button, not on the li. In the Demo, the first and second lis and buttons work correctly. Would you please tell me how to change the code when click on li button and popover show up after the third li. Thanks your help. – user2150267 Jun 07 '13 at 20:38
  • @user2150267 if you want it on button you could just do `$(event.target).find('button[rel=popover]').popover(popOverSettings);`. But i am unsure how you are appending new li's but try this. – PSL Jun 07 '13 at 20:41
  • @user2150267 Let me know if this doesn't work. But i would suggest you to go with option 1 without using any mutation (read through the link in my answer you will know, support could be aconcern in older IE i guess). If you can show me in a fiddle what you are doing while appending the new elements i can provide you better inputs.. – PSL Jun 07 '13 at 20:52
  • Thanks your help, but I still can't figure out how to work the popover show up when I click on the button, not on the li. – user2150267 Jun 07 '13 at 20:53
  • @user2150267 check my previous comment fiddle – PSL Jun 07 '13 at 20:56
  • yep. I tired to use $(event.target).find('button[rel=popover]').popover(popOverSettings); but It doesn't work. – user2150267 Jun 07 '13 at 20:57
  • Not event.target. See the mutation event in my fiddle updated.`$(mutation.addedNodes).find('button[rel=popover]').popover(popOverSettings);` – PSL Jun 07 '13 at 20:59
  • hi PSL it works correctly when I use $(event.target).find('button[rel=popover]').popover(popOverSettings); on demo one. I will try $(mutation.addedNodes).find('button[rel=popover]').popover(popOverSetti‌​ngs); on demo two. Thank you. – user2150267 Jun 07 '13 at 21:09
  • $(mutation.addedNodes).find('button[rel=popover]').popover(popOverSetti‌​ngs); works correctly on the demo two. would you please update $(event.target).find('button[rel=popover]').popover(popOverSettings) for demo one , and $(mutation.addedNodes).find('button[rel=popover]').popover(popOverSetti‌​ngs); for demo two on the fiddle. Thanks your help. really appreciated. – user2150267 Jun 07 '13 at 21:22
  • @Mrchief Thank you!! :) – PSL Jul 28 '15 at 13:35
  • `data-trigger=focus` doesn't seem to work on this, ie I need to click the same check-box in order to close the popover. – Praful Bagai Sep 21 '16 at 14:12
16

Probably way too late but this is another option:

 $('body').popover({
    selector: '[rel=popover]',
    trigger: 'hover',
    html: true,
    content: function () {
        return $(this).parents('.row').first().find('.metaContainer').html();
    }
});
Cipriano
  • 161
  • 1
  • 3
6

I did this and it works for me. "content" is placesContent object. not the html content!

var placesContent = $('#placescontent');
$('#places').popover({
        trigger: "click",
        placement: "bottom",
        container: 'body',
        html : true,
        content : placesContent,
    });

$('#places').on('shown.bs.popover', function(){
  $('#addPlaceBtn').on('click', addPlace);
}

<div id="placescontent"><div id="addPlaceBtn">Add</div></div>
alex
  • 479,566
  • 201
  • 878
  • 984
nurp
  • 1,239
  • 2
  • 14
  • 23
4

Try this HTML

<a href="#" data-toggle="popover" data-popover-target="#popover-content-1">Do Popover 1</a>
<a href="#" data-toggle="popover" data-popover-target="#popover-content-2">Do Popover</a>

<div id="popover-content-1" style="display: none">Content 1</div>
<div id="popover-content-2" style="display: none">Content 2</div>

jQuery:

$(function() {
  $('[data-toggle="popover"]').each(function(i, obj) {
    var popover_target = $(this).data('popover-target');
    $(this).popover({
        html: true,
        trigger: 'focus',
        placement: 'right',
        content: function(obj) {
            return $(popover_target).html();
        }
    });
  });
});
Joundill
  • 6,828
  • 12
  • 36
  • 50
Somwang Souksavatd
  • 4,947
  • 32
  • 30
1

This is how I made the code so it can handle dynamically created elements using popover feature. Using this code, you can trigger the popover to show by default.

HTML:

<div rel="this-should-be-the-target">
</div>

JQuery:

$(function() {
    var targetElement = 'rel="this-should-be-the-target"';
    initPopover(targetElement, "Test Popover Content");

    // use this line if you want it to show by default
    $(targetElement).popover('show');
    
    function initPopover(target, popOverContent) {
        $(target).each(function(i, obj) {
            $(this).popover({
                placement : 'auto',
                trigger : 'hover',
                "html": true,
                content: popOverContent
            });
         });
     }
});
Dharman
  • 30,962
  • 25
  • 85
  • 135
1

Just to update that, a no-jquery answer for people using bootstrap 5 is

var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
popoverTriggerList.map(function (popoverTriggerEl) {
    return new bootstrap.Popover(popoverTriggerEl, {
        content: get_content
    })
})

The get_content function should use this (the popover element) to generate dynamic content.

user2283347
  • 670
  • 8
  • 12