How to bind bootstrap popover on dynamic elements
Asked Answered
X

6

64

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.

Xerox answered 7/6, 2013 at 18:21 Comment(1)
Dupe. The answer here is simpler and worked just fine #18731594Haplite
D
106

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

Disregard answered 7/6, 2013 at 19:4 Comment(11)
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.Xerox
@Xerox 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.Disregard
@Xerox 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..Disregard
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.Xerox
@Xerox check my previous comment fiddleDisregard
yep. I tired to use $(event.target).find('button[rel=popover]').popover(popOverSettings); but It doesn't work.Xerox
Not event.target. See the mutation event in my fiddle updated.$(mutation.addedNodes).find('button[rel=popover]').popover(popOverSettings);Disregard
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.Xerox
$(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.Xerox
@Mrchief Thank you!! :)Disregard
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.Nerin
B
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();
    }
});
Beery answered 18/6, 2015 at 11:5 Comment(0)
M
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>
Morrismorrison answered 18/9, 2013 at 23:17 Comment(0)
T
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();
        }
    });
  });
});
Toole answered 30/8, 2018 at 17:46 Comment(0)
M
2

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.

Marabou answered 15/11, 2021 at 17:9 Comment(0)
L
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
            });
         });
     }
});
Lipski answered 12/8, 2020 at 5:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.