Show and hide dynamic jQuery UI Dialogs - javascript

I know this should be simple, but it doesn't appear to be working the way I hoped it would.
I'm trying to dynamically generate jQuery UI dialogs for element "help."
I want to toggle the visibility of the dialog on close (x button in dialog), and clicking of the help icon. This way, a user should be able to bring up the dialog and get rid of it, as needed, multiple times during a page view.
// On creation of page, run the following to create dialogs for help
// (inside a function called from document.ready())
$("div.tooltip").each(function (index, Element) {
$(Element).dialog({
autoOpen: false,
title: $(Element).attr("title"),
dialogClass: 'tooltip-dialog'
});
});
$("a.help").live("click", function (event) {
var helpDiv = "div#" + $(this).closest("span.help").attr("id");
var dialogState = $(helpDiv).dialog("isOpen");
// If open, close. If closed, open.
dialogState ? $(helpDiv).dialog('close') : $(helpDiv).dialog('open');
});
Edit: Updated code to current version. Still having an issue with value of dialogState and dialog('open')/dialog('close').
I can get a true/false value from $(Element).dialog("isOpen") within the each. When I try to find the element later (using a slightly different selector), I appear to be unable to successfully call $(helpDiv).dialog("isOpen"). This returns [] instead of true/false. Any thoughts as to what I'm doing wrong? I've been at this for about a day and a half at this point...

Maybe replace the line declaring dialogState with var dialogState = ! $(helpDiv).dialog( "isOpen" );.
Explanation: $(helpDiv).dialog( "option", "hide" ) does not test if the dialog is open. It gets the type of effect that will be used when the dialog is closed. To test if the dialog is open, you should use $(helpDiv).dialog( "isOpen" ). For more details, see http://jqueryui.com/demos/dialog/#options and http://jqueryui.com/demos/dialog/#methods.

I was able to get it working using the following code:
$("div.tooltip").each(function (index, Element) {
var helpDivId = '#d' + $(Element).attr('id').substr(1);
var helpDiv = $(helpDivId).first();
$(Element).dialog({
autoOpen: true,
title: $(Element).attr("title"),
dialogClass: 'tooltip-dialog'
});
});
// Show or hide the help tooltip when the user clicks on the balloon
$("a.help").live("click", function (event) {
var helpDivId = '#d' + $(this).closest('span.help').attr('id').substr(1);
var helpDiv = $(helpDivId).first();
var dialogState = helpDiv.dialog('isOpen');
dialogState ? helpDiv.dialog('close') : helpDiv.dialog('open');
});
I changed the selectors so that they're identical, instead of just selecting the same element. I also broke out the Id, div and state into separate variables.

Related

How to check whether a twitter bootstrap popover is visible or not?

I have an element $('#anElement') with a potential popover attached, like
<div id="anElement" data-original-title="my title" data-trigger="manual" data-content="my content" rel="popover"></div>
I just would like to know how to check whether the popover is visible or not: how this can be accomplished with jQuery?
If this functionality is not built into the framework you are using (it's no longer twitter bootstrap, just bootstrap), then you'll have to inspect the HTML that is generated/modified to create this feature of bootstrap.
Take a look at the popupver documentation. There is a button there that you can use to see it in action. This is a great place to inspect the HTML elements that are at work behind the scene.
Crack open your chrome developers tools or firebug (of firefox) and take a look at what it happening. It looks like there is simply a <div> being inserted after the button -
<div class="popover fade right in" style="... />
All you would have to do is check for the existence of that element. Depending on how your markup is written, you could use something like this -
if ($("#popoverTrigger").next('div.popover:visible').length){
// popover is visible
}
#popoverTrigger is the element that triggered that popover to appear in the first place and as we noticed above, bootstrap simply appends the popover div after the element.
There is no method implemented explicitly in the boostrap popover plugin so you need to find a way around that. Here's a hack that will return true or false wheter the plugin is visible or not.
var isVisible = $('#anElement').data('bs.popover').tip().hasClass('in');
console.log(isVisible); // true or false
It accesses the data stored by the popover plugin which is in fact a Popover object, calls the object's tip() method which is responsible for fetching the tip element, and then checks if the element returned has the class in, which is indicative that the popover attached to that element is visible.
You should also check if there is a popover attached to make sure you can call the tip() method:
if ($('#anElement').data('bs.popover') instanceof Popover) {
// do your popover visibility check here
}
In the current version of Bootstrap, you can check whether your element has aria-describedby set. The value of the attribute is the id of the actual popover.
So for instance, if you want to change the content of the visible popover, you can do:
var popoverId = $('#myElement').attr('aria-describedby');
$('#myElement').next(popoverid, '.popover-content').html('my new content');
This checks if the given div is visible.
if ($('#div:visible').length > 0)
or
if ($('#div').is(':visible'))
Perhaps the most reliable option would be listening to shown/hidden events, as demonstrated below. This would eliminate the necessity of digging deep into the DOM that could be error prone.
var isMyPopoverVisible = false;//assuming popovers are hidden by default
$("#myPopoverElement").on('shown.bs.popover',function(){
isMyPopoverVisible = true;
});
$("#myPopoverElement").on('hidden.bs.popover',function(){
isMyPopoverVisible = false;
});
These events seem to be triggered even if you hide/show/toggle the popover programmatically, without user interaction.
P. S. tested with BS3.
Here is simple jQuery plugin to manage this. I've added few commented options to present different approaches of accessing objects and left uncommented that of my favor.
For current Bootstrap 4.0.0 you can take bundle with Popover.js: https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.bundle.min.js
// jQuery plugins
(function($)
{
// Fired immiedately
$.fn.isPopover = function (options)
{
// Is popover?
// jQuery
//var result = $(this).hasAttr("data-toggle");
// Popover API
var result = !!$(this).data('bs.popover');
if (!options) return result;
var $tip = this.popoverTip();
if (result) switch (options)
{
case 'shown' :
result = $tip.is(':visible');
break;
default:
result = false;
}
return result;
};
$.fn.popoverTip = function ()
{
// jQuery
var tipId = '#' + this.attr('aria-describedby');
return $(tipId);
// Popover API by id
//var tipId = this.data('bs.popover').tip.id;
//return $(tipId);
// Popover API by object
//var tip = this.data('bs.popover').tip; // DOM element
//return $(tip);
};
// Load indicator
$.fn.loadIndicator = function (action)
{
var indicatorClass = 'loading';
// Take parent if no container has been defined
var $container = this.closest('.loading-container') || this.parent();
switch (action)
{
case 'show' :
$container.append($('<div>').addClass(indicatorClass));
break;
case 'hide' :
$container.find('.' + indicatorClass).remove();
break;
}
};
})(jQuery);
// Usage
// Assuming 'this' points to popover object (e.g. an anchor or a button)
// Check if popover tip is visible
var isVisible = $(this).isPopover('shown');
// Hide all popovers except this
if (!isVisible) $('[data-toggle="popover"]').not(this).popover('hide');
// Show load indicator inside tip on 'shown' event while loading an iframe content
$(this).on('shown.bs.popover', function ()
{
$(this).popoverTip().find('iframe').loadIndicator('show');
});
Here a way to check the state with Vanilla JS.
document.getElementById("popover-dashboard").nextElementSibling.classList.contains('popover');
This works with BS4:
$(document).on('show.bs.tooltip','#anElement', function() {
$('#anElement').data('isvisible', true);
});
$(document).on('hidden.bs.tooltip','#anElement', function() {
$('#anElement').data('isvisible', false);
});
if ($('#anElement').data('isvisible'))
{
// popover is visible
$('#tipUTAbiertas').tooltip('hide');
$('#tipUTAbiertas').tooltip('show');
}
Bootstrap 5:
const toggler = document.getElementById(togglerId);
const popover = bootstrap.Popover.getInstance(toggler);
const isShowing = popover && popover.tip && popover.tip.classList.contains('show');
Using a popover with boostrap 4, tip() doesn't seem to be a function anymore. This is one way to check if a popover is enabled, basically if it has been clicked and is active:
if ($('#element').data('bs.popover')._activeTrigger.click == true){
...do something
}

Update a jQuery script when user switches to a new tabbed element

I'm having a hard time describing exactly what the problem is.. but the link in question is: http://www.evolutionarycollective.com/events/
You'll notice that when you load the "Calendar" tab, the calendar doesn't show up. If you resize the window, or manipulate the page in some other way, then the calendar appears. The calendar loads fine when it's not within a tab. It's being called in the header with:
<script type='text/javascript'>
jQuery(document).ready(function() {
jQuery('#calendar').fullCalendar({
events: themeforce.events
});
});
</script>
EDIT:
I believe this is the section of the script library that handles the tabs and panes.. (I'm using this with a wordpress theme that calls the tabs via a shortcode)
// jQuery tool's tab creator is not shortcode friendly, transfer the titles to the correct tabs area
jQuery('.bfi_pane').each(function(i) {
var title = jQuery(this).attr('title');
jQuery(jQuery(this).siblings('.bfi_tabs,.bfi_tabs_slide,.bfi_tabs_fade')[0]).append('<div>'+title+'</div>');
});
// Custom slide effect for tabs. slide up then down
jQuery.tools.tabs.addEffect('slide-slide',function(i, done) {
this.getPanes().slideUp(400).eq(i).delay(400).slideDown(400, done);
});
// Custom slide effect for tabs. slide up and down simultaneously
jQuery.tools.tabs.addEffect("slide", function(i, done) {
this.getPanes().slideUp(400).eq(i).slideDown(400, done);
});
// IMPORTANT: SLIDEDOWN JQUERY FIX. we need to assign the correct heights
// so that the slidedown effect doesn't JUMP
jQuery(".bfi_accordion_pane").each(function(i) {
var heightTo = jQuery(this).height();
var paddingTop = parseInt(jQuery(this).css("padding-top").replace('px', ''));
var paddingBottom = parseInt(jQuery(this).css("padding-bottom").replace('px', ''));
var marginTop = parseInt(jQuery(this).css("margin-top").replace('px', ''));
var marginBottom = parseInt(jQuery(this).css("margin-bottom").replace('px', ''));
jQuery(this).css("height", heightTo + paddingTop + paddingBottom + marginTop + marginBottom);
});
// start jQuery tool tabs. we have to do this the long way so we can make
// initialIndex work properly
jQuery(".bfi_tabs_slide").each(function(i) {
var openTab = jQuery(this).attr('rel');
jQuery(this).tabs('> div.bfi_pane', {effect: 'slide-slide', initialIndex: parseInt(openTab, 10)});
});
jQuery(".bfi_tabs_fade").each(function(i) {
var openTab = jQuery(this).attr('rel');
jQuery(this).tabs('> div.bfi_pane', {effect: 'fade', initialIndex: parseInt(openTab, 10)});
});
Is there some way I can somehow refresh the script when that tab lis loaded.. or fire off another document.ready?
Thank you!
The probable explanation is that the full calendar can't figure out its height because when initially created that tab is hidden, which will make it have zero width and height.
Just make the call to .fullCalendar() in a tab switch event handler.
Alternatively use the "off-left" method of hiding tabs, as described in the last few paragraphs of http://jqueryui.com/demos/tabs/

Custom alert using Javascript

How can I create a custom alert function in Javascript?
You can override the existing alert function, which exists on the window object:
window.alert = function (message) {
// Do something with message
};
This is the solution I came up with. I wrote a generic function to create a jQueryUI dialog. If you wanted, you could override the default alert function using Matt's suggestion: window.alert = alert2;
// Generic self-contained jQueryUI alternative to
// the browser's default JavaScript alert method.
// The only prerequisite is to include jQuery & jQueryUI
// This method automatically creates/destroys the container div
// params:
// message = message to display
// title = the title to display on the alert
// buttonText = the text to display on the button which closes the alert
function alert2(message, title, buttonText) {
buttonText = (buttonText == undefined) ? "Ok" : buttonText;
title = (title == undefined) ? "The page says:" : title;
var div = $('<div>');
div.html(message);
div.attr('title', title);
div.dialog({
autoOpen: true,
modal: true,
draggable: false,
resizable: false,
buttons: [{
text: buttonText,
click: function () {
$(this).dialog("close");
div.remove();
}
}]
});
}
Technically you can change what the alert function does. But, you cannot change the title or other behavior of the modal window launched by the native alert function (besides the text/content).
If you're looking for a javascript/html/css replacement, I recommend checking out jQueryUI and its implementation of modal dialogs.
"override" way is not good enough, suggest you to create a custom popup box. The best benefit of this solution is that you can control every details.
No, you can not using the default alert.
Not even formating.
But, I recomend you using Sweet alert to do that.

How to put buttons in a html page in a jQuery dialog

eHello everyone,the following is my code to display a jquery dialog window with a closing button "OK":
<script type="text/javascript">
$(function(){
$("#dialog").dialog({
autoOpen:false,
bgiframe:true,
buttons: { "OK": function() { $(this).dialog("close"); } },
width:500,
height: 350,
modal: true,
show: 'slide',
hide:'slide',
title:"Similar Trends Detected in 2nd DataSet"
});
$("#userid").focus();
});
function showForm(matches){
$("#dialog").html(matches).dialog("open");
}
Currently it runs by supplying a string variable "matches",then the content of the variable gets displayed on the dialog frame.
Now me and my teammate want to extend this dialog a little,we want to attach a button to every line inside the html content("matches" variable),please note that we don't want buttons in the dialog(like another "OK" button),but we want buttons "inside" the frame (the actual html content).
So I would like some help here,how could I modify my "matches" variable,to have buttons also shown inside the dialog.
Thanks.
EDIT: Updated based on comments from OP
function showForm(matches){
// Of course, you'll need to modify with your own button.
// I also added a valid <br>, assuming you want it there.
matches = matches.replace( /<\/br>/g, '<button>my button</button><br>' );
$("#dialog").html( matches ).dialog("open"); // Insert new HTML content
}
Does the matches variable contain HTML?
You could just make a jQuery object out of it, and traverse it like any other HTML:
function showForm(matches){
// Of course, you'll need to modify with your own button.
// I also added a valid <br>, assuming you want it there.
matches = matches.replace( /<\/br>/g, '<button>my button</button><br>' );
$("#dialog").html( matches ).dialog("open"); // Insert new HTML content
}
Relevant jQuery docs:
.after() - http://api.jquery.com/after/
.find() - http://api.jquery.com/find/
Traversing: http://api.jquery.com/category/traversing/
what do you mean by every line? can you post a sample value for the matches variable? why not just include the buttons in the matches string value?
anyway, you can also provide a callback function to the dialog widget's 'open' event.
$("#dialog").dialog({
autoOpen:false,
bgiframe:true,
buttons: {
"OK": function() {
$(this).dialog("close");
}
},
width:500,
height: 350,
modal: true,
show: 'slide',
hide:'slide',
title:"Similar Trends Detected in 2nd DataSet",
open: function() {
var targetElements = 'br';
$(this).find(targetElements).after('<button>click me</button>');
}
});
after every br tag in the content, a button will be appended after it... every time the dialog is shown, the open callback will be triggered.
So the matches content is some static set of HTML. Once it has been added to the DOM you can use the same selectors and controls you use for everything else. So let us assume for the moment that the matches field contains a list of elements.
function showForm(matches){
$("#dialog").html(matches).dialog("open");
var b = $("<input type='button' value='clickme'/>");
$("#dialog ul li").append(b);
}
Of course this is only really going to work if you have some conception of what match contains. If you know for example that it is a set of divs with a certain class that will help in making the selector.

Combining multiple jQuery functions

Is there any way to combine all of this to reduce the amount of js? This is just an example of some of the jquery dialogs I have in my site, there are a few more too. Thanks.
//initiate Search refinement dialog here
$("#chooseMoreCnt, #chooseMoreCat, #chooseMorePr").dialog({
bgiframe: true,
autoOpen: false,
width: 500,
modal: true,
open: function(type, data) {
$(this).parent().appendTo(jQuery("form:first"));
}
});
//trigger country dialog
$('a.chooseMoreCnt').click(function() {
$('#chooseMoreCnt').dialog('open');
return false;
});
//trigger category dialog
$('a.chooseMoreCat').click(function() {
$('#chooseMoreCat').dialog('open');
return false;
});
//trigger price dialog
$('a.chooseMorePr').click(function() {
$('#chooseMorePr').dialog('open');
return false;
});
If your links point to the IDs of the dialog elements, and if you add a meta class choose to each of them, you could combine the last three calls to:
$('a.choose').click(function() {
$(this.hash).dialog('open');
return false;
});
The HTML for one of those links is the most semantically correct and even works with JS disabled (assuming, the dialogs are there, then):
Choose more categories
The this.hash part explained:
this in the context of a jQuery event handling function is always the element, that the event appeared at. In our case, it's the clicked link. Note, that it's the DOM node, not a jQuery element.
this.hash: DOM nodes, that correspond to HTML <a/> elements, have certain special properties, that allow access to the target they're linking to. The hash property is everything after (and including) an # character in the URL. In our case, if the link points to the elements that should become dialogs, it's something like the string "#chooseMoreCnt".
$(this.hash) is the jQuery function called for, e.g., "#chooseMoreCnt", which will select the appropriate div.
For the dialog initialization, I would also go for classes:
$(".choose_dialog").dialog({
bgiframe: true,
autoOpen: false,
width: 500,
modal: true,
open: function(type, data) {
$(this).parent().appendTo(jQuery("form:first"));
}
});
Yes, it means to change the markup, but it also provides you with the freedom to
add any number of dialogs lateron
add any number of openers to any dialog lateron
style all dialogs and links to dialogs consistantly with minimal CSS
without touching the Javascript anymore.
If the dialogs are initiated differently (as mentioned in the comments), then you could go for this part with CuSS's $.each() approach and read the appropriate width inside the function from an object defined elsewhere:
var dialog_widths = {'chooseMoreCat': 400, 'chooseMorePr': 300, /*...*/ };
This is what I would suggest. Specify a general DialogContent (say) class to all the divs and initialize them using:
$(".dialogContent").dialog({
bgiframe: true,
autoOpen: false,
width: 500,
modal: true,
open: function(type, data) {
$(this).parent().appendTo(jQuery("form:first"));
}
});
And ofcourse use Boldewyn's solution for click event (it is better to use live() IMHO if things are getting dynamically generated). This way you take care of all initializations and click events with way less code.
HTH
well, this is a little complicated to minimize.
do you have more than 3 dialogs? If yes you can do something like this:
var dialogs=["chooseMorePr","chooseMoreCat","chooseMoreCnt"];
$.each(dialogs,function(i,v){
$('a.'+v).click(function(){$('#'+v).dialog('open');});
});
In order to optimize performance, you should use live when connecting to several elements. Below is my approach to the problem. The solution is dynamic (add as many dialogues as you want to) and very speedy.
Remember to change #anyParentOfTheLinks into the parent div or in worst case remove it and jQuery will use document instead.
var dialogues = ['#chooseMoreCnt', '#chooseMoreCat', '#chooseMorePr'];
$(dialogues.toString()).dialog({
// ...
});
$('a', '#anyParentOfTheLinks').live('click', function(){
// Cache for performance
var $this = $(this), len = dialogues.length;
for(var i = 0; i < len; i++)
if($this.is('.' + dialogues[i].substr(1))) {
$this.dialog('open');
break;
}
return false;
});

Categories

Resources