23
Dec
2015

Dynamically Creating Bootstrap Popovers With Javascript

posted Wednesday, December 23rd 2015 at 9:52 AM by

Dynamically Create Bootstrap Popovers with Javascript

Bootstrap includes a popover library that lets developers easily create styled messages that can be displayed to users by triggering some javascript. I extended that functionality a little bit so that users can close the popover messages by clicking on them, and developers can dynamically create popover messages with a simple function call.

Objective

I developed this library to enhance Bootstrap's popover functionality. I specifically wanted to:

  1. Allow users to close popovers just by clicking on them.
  2. Enable developers to dynamically create popovers.
  3. Include an option for developers to choose how to trigger popovers.
  4. Include an options for developers to display popovers immediately, without needing a user action.

How Does It Work?

Bootstrap triggers it's popover items by calling the .popover("show") from the DOM element that the popover was created on. Unfortunately, there is no reference from the popover back to the element on which it was created.

We get around that by giving each element that has a popover a unique identifier saved as the 'data-owner' attribue. Then we dynamically create a 'data-ownedby' attribute on each popover as they are created. This attribute is tied back to the element that owns the popover and is used to help us close the popover by clicking on it. All of this is handled by a custom createPopover() function, detailed below.

function createPopover(selector,title,content,trigger,autoshow)

Overview

This function creates bootstrap popovers, and ties the popovers themselves to the element that called the popover.

Parameters

selector - this is the CSS selector that popovers will be added to. e.g. "#submit-button", "a.show-details", "div#faq li.question".

title - the title of the bootstrap popover. Bootstrap wraps this in an h3 tag.

content - the content of the bootstrap popover.

trigger - the event that triggers the popover. e.g. "click", "hover focus".

autoshow - when true, shows the popover immediately. Possible values are either true or false.

Return Values

The function returns boolean true if it was able to create the popover on a DOM element(s), or returns boolean false if it was unable to find elements matching the CSS selector.

Javascript Code

$(document).ready(function() {

	//keep track of how many popovers exist, we'll need this to track owner/ownedby relationships
	numberOfPopovers = $("[data-toggle='popover']").length;

	createPopover("button","Try It Out!","Click here to create your own dynamically generated popover.","hover",true);
    
});
var numberOfPopovers;


function addPopover() {
	selector = prompt('To which CSS selector should we add the popover?');
	title = prompt('What is the title for the popover?');
	content = prompt('What is the content for the popover?');
	trigger = "click";
	autoshow = confirm("Show this popover immediately?");
	if(createPopover(".safe-zone "+selector,title,content,trigger,autoshow) == false) {
		alert("Unable to add popover to CSS selector '" + selector +"'.");
	}
}


function createPopover(selector,title,content,trigger,autoshow) {
	//check to see if the dom has elements matching the selector, and they are not elements that already have a popover i.e. they dont have data-toggle='popover'
	selector = selector + ":not([data-toggle='popover'])";
	domHasElements = $(selector+":not([data-toggle='popover'])").length > 0;

	//if we have elements, lets go!
	if(domHasElements) {

		//for each matching element, add the popover
		$(selector).each(function() {
			//track the owner
    		var popOwner = 'popOwner' + numberOfPopovers;
        	
        	//add the bootstrap data-toggle attribute to the element
        	$(this).attr('data-toggle','popover');

        	//add the popover title to the element
	        $(this).attr('data-title',title);

	        //add the popover content to the element
			$(this).attr('data-content',content);

			//add the owner to the element
			$(this).attr('data-owner',popOwner);



			//add popover
			$(this).popover({placement: "left", container: "body", html:"true", delay: 0, trigger: "manual"});

			//add hover bindings
			if(trigger.indexOf("hover") >= 0) {
				$(this).hover(function() {
					//hover over binding
					isCurrentlyHidden = $('.popover[data-ownedby="'+popOwner+'"]').length == 0;
					//if its already visible, dont do any animations
					if(isCurrentlyHidden) {
						$(this).popover('show');
					}
				},function() {
					//hover out binding
					$(this).popover('hide');
				});
			}

			//add focus bindings
			if(trigger.indexOf("focus") >= 0) {
				$(this).focus(function() {
					//gained focus binding
					isCurrentlyHidden = $('.popover[data-ownedby="'+popOwner+'"]').length == 0;
					//if its already visible, dont do any animations
					if(isCurrentlyHidden) {
						$(this).popover('show');
					}
				});
				$(this).blur(function() {
					//lost focus binding
					$(this).popover('hide');
				});
			}

			//add click bindings
			if(trigger.indexOf("click") >= 0) {
				$(this).bind(trigger,function() {
					$(this).popover('toggle');
				});
			}


			//bind the click event to popover items as they are created.
			$(this).on('shown.bs.popover', function() {
				
				// bootstrap does not connect popovers and their triggering elements, all will use the .popover class.
				// we need to match the owner element with its corresponding popover element

				//get the element that triggers the popover				
				popOwner = $(this).attr('data-owner');
				var owner = $('[data-owner="'+popOwner+'"]');

				//in case multiple popovers are triggered at once, add the ownedby attribute to the first one that doesnt already have the ownedby attribute
				$('.popover:not([data-ownedby]):first').attr('data-ownedby',popOwner);


				
				//bind the click event to that popover
				$('.popover[data-ownedby="'+popOwner+'"]').bind('click',function() {
					owner.popover("hide");
				});
			});
			
			

			//immediately show any popovers set to autoshow
			if(autoshow) { $(this).popover("show"); }

			//increment the counter
    		numberOfPopovers++;

		});
		return true;
	} else {
		//if there were no elements found, or the element already had a popover attached, return false
		return false;
	}

}

Example

You can see the code in action here:

paragraph tag with an id.

a span tag with an id here.

an anchor tag and another span tag

Here is the HTML for the test area. You can use the tag names, ids, and classes to test different popover attachments.

paragraph tag with an id.

a span tag with an id here.

an anchor tag and another span tag

Summary

Overall a pretty simple and handy function to enhance bootstrap popovers. If you find it useful or have questions, let me know in the comments!

Share This:

Tags:

Comments:

View (2) Comments Post a Comment
  • Replying to Adam Konieska on Dynamically Creating Bootstrap Popovers With Javascript







  • nathan cain
    Nathan Cain
    Tuesday, November 15th 2016 at 12:05 PM

    This is just what I need. I'm going to give it a try. Thanks.

    • Replying to Nathan Cain







  • nathan cain
    Nathan Cain
    Wednesday, November 16th 2016 at 7:48 AM

    Works great! Thanks again.

    How do I make it so that it doesn't close when I click on the popover.

    • Replying to Nathan Cain