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;
});
Related
I am trying to use 2 jQuery navigation scripts on one page (Superfish for desktops and FlexNav for mobile). I am currently using matchMedia along with the polyfill by Paul Irish to respond to CSS3 media query state changes within JavaScript.
The current code is only accomplishing 50% of the overall goal. If you access the web page initially with a window size equal to or greater than 999px wide then you get Superfish and if you initially access the web page with a window size less than 999px then you get FlexNav. The problem occurs when you resize the window above or below 999px as both scripts become active.
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
$("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
$("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
As much as I would like to get this working with matchMedia, I am open to all suggestions.
Update: Thanks to Stephan's suggestion I now have the following code:
jQuery(document).ready(function () {
// add destroy function for FlexNav
flexNavDestroy = function () {
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
}
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
if (typeof (flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if (typeof (superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
});
Remaining Issue:
The destroy function for FlexNav is only partially destroying it.
The best way would probably be to destroy the other plugin when you're activating one.
If I look in the source of Superfish there is a destroy function which does this, but flexNav doesn't have such a function. You can create one though:
flexNavDestroy = function(){
$('.touch-button').off('touchstart click').remove();
$(('.item-with-ul *').off('focus');
}
Then you could do this:
function WidthChange(mq) {
if (mq.matches) {
if(typeof(flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if(typeof(superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
UPDATE
I've looked a little bit more into FlexNav, and there's a few things I missed.
I think the styles are colliding because FlexNav sets a lot of styles by default. We can easily prevent that by using two classes: One for flexnav styling (the default .flexnav) that we can remove to hide all it's styles, and one for binding the javascript function (that will always stay there, or we can't re-attach it).
I generally like to prepend any classes that are meant as JS hooks with js-, so in my example (below) I replaces the .flexnav class on the menu with .js-flexnav. Then to activate flexnav you have to add this line just before you call $('ul.flexnav').flexNav()
$('.js-flexnav').addClass('flexnav');
In the destroy function you will have to remove the class again, which I will show shortly.
In addition, I'm not sure how Superfish does the showing and hiding, but since FlexNav collapses all submenus, it's also safe to say you should re-show them so that Superfish can do it's own thing.
The updated destroy function to reflect this:
function flexNavDestroy(){
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
$('.js-flexnav').removeClass('flexnav').find('ul').show(); // removes .flexnav for styling, then shows all children ul's
}
Here's a jsFiddle that shows activating/deactivating flexNav with the new code: http://jsfiddle.net/9HndJ/
Let me know if this does the trick for you!
here is an alternative path :
once page is loaded :
cache the menu in a jquery object, clone it & instantiate both plugin one on each clone
$menucontainer= $("#menu_container");
$memufish = $menucontainer.find(".menu");
$menuflex=$menufish.clone();
$menufish.superfish().detach();
$menuflex.prependTo($menucontainer).flexnav().detach();
(they are loaded anyway so it's no big deal even if most of the time one won't be needed, it will be there & ready just in case - however test if you can instantiate on the clone without appending it to the DOM)
depending on width append / prepend the required one
$menuflex.prependTo($menucontainer);
on change width detach one reattach the other
$menufish.detach();
$menuflex.prependTo($menucontainer);
you could also work your way checking if plugin was instantiated on a width change (in order to not instantiate uselessly onload) but in any way I believe the use of clone() and detach() are very much adapted to solve easily your problem. The destroy way seems to be a hassle, lots of work (for the script as well when some user is raving with window resize) loss of time & a risk of many bugs to me ( expect more and more lag at every destroy re instantiate - with detach() no worries)
cons : will use a bit more memory overhaul
pros :
script will work less & it will be real fast to switch from one to the other
you could make a plugin from this and add other menu plugin to your app very easily without worry about conflict and how to destroy
We have a number of jQuery DataTables that all use server side processing. We have paging and sorting set up, and all is working well. In these tables there is at least one column of checkboxes to allow selecting of rows to do some kind of processing on. We want to confirm that the user wishes to page or sort if there are any checkboxes checked.
What I thought I could do (and can't)
"fnPreDrawCallback" : function(table) {
if (CullAddress.AddressIsChecked()) {
var $warningDiv = $('div#pageWarning');
var warningText = "One or more Addresses are selected for Excluding or Tagging. Are you sure you wish to nvaigate away?";
$warningDiv.find("div#pageWarningText").html(warningText);
$warningDiv.dialog({
resizable: false,
height: "auto",
width: "auto",
modal: true,
buttons: {
"Leave Page": function () {
CullAddress.resetWarningText();
$warningDiv.dialog('close');
},
"Stay On Page": function () {
CullAddress.resetWarningText();
$warningDiv.dialog('close');
return false;
}
},
});
}
},
Initially I thought this would be simple, but now, it is getting a bit hefty, and I am not sure of what the right way is. I am trying to use the fnPreDrawCallback, and initially I intended to create and display a jQueryUI Dialog, and have the buttons determine whether or not to return false; out of the callback thus staying on the page, or allowing the page/sort to go through.
I now understand that javaScript does not work that way. I suspect I will have to do the following, but before I go through that trouble I want to ask if there is a more concise (and reusable) way of doing this.
In fnPreDrawCallback, get properties to describe the intended set page/sort (e.g. offset, pageSize, sSortDir, iSortCol, etc).
Determine via dialog whether to continue or stay on page
Use aforementioned properties to construct the GET for the datatable to bypass the fnPreDrawCallback
Am I making this more difficult that it needs to be? Surely I am not the first person to want to do this, but for the life of me, I can find an example, or I cant figure out the keywords I should be searching for...
Any helps?
Link to working example: http://jsfiddle.net/6frQZ/3/
As already discussed in the comments to the question, I tried to circumvent the default behaviour of DataTables to fit your needs and created an example on jsFiddle to show, including numbered-pagination and sorting.
Basically, you'll need to unbind the event-handlers, that the DataTables - plugin binds to it's components, like so:
$('.dataTables_paginate a').unbind();
$('.dataTables_wrapper thead th').unbind();
Using .unbind without a parameter will unbind any event-listener on the element, so be careful when using this.
Gladly, the DataTables - API provides functions that let you call the internal paging and sorting-methods yourself, named fnSort (API-Link) and fnPageChange (API-Link).
To keep it simple, i just used a confirm - Box to ask for the user-interaction:
var userInteraction = confirm("Do you really want to change the page?");
if(userInteraction){
oTable.fnPageChange(dir);
$('.dataTables_paginate span a').unbind();
}
but all you'd need to do is call the DataTables-functions inside of your "Leave Page" - callback you already provided in the code.
Note: When it comes to the numbered buttons of the paging: It seems like DataTables regenerates those everytime the paging is changed, thus I need to unbind the event-Handlers again after every page-change.
The rest is simple yet not very elegant code, in which I just look for certain classes to know, what button was clicked or which state the sorting-header is in.
Excerpt:
var dir = "",
$this = $(this);
if($this.hasClass('previous')){
dir = "previous";
}else if ($this.hasClass('next')){
dir = "next";
}else if($this.hasClass('first')){
dir = "first";
}else if($this.hasClass('last')){
dir = "last";
}else{
dir = parseInt($this.text(),10)-1;
}
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.
I want to show a popup many on click. I want that many to be in a bubble. So I created a demo: here. But that Bubble generator plugin i use tends to keep tons of trash in the DOM each time it shows a popup. Well so I tried to destroy trash via
$('.grumble-text').remove();
$('.grumble').remove();
$('.grumble-button').remove();
But it somehow brakes it at all=( So how to change grumble-bubble popup plugin code to make it either keep DOM clean or at least make plugin independent of trash it creates?
I've recently updated the plugin to provide better control of positioning and angle. The update also persists the grumble, invoking the plugin more than once on an element will not create extra left over DOM.
Try updating to the latest code. The code below should now work as you expect.
var html = ''
+'Download me'
+'<br/>'
+'Edit me'
+'<br/>'
+'Delete me';
var $grumble = $('#grumble3');
$grumble.mouseup(function(eventObj) {
$grumble.grumble({
text: html ,
angle: (Math.random() * 360 + 150),
distance: 30,
hideOnClick: true,
onShow: function() {
$grumble.addClass("hilight");
},
onBeginHide: function() {
$grumble.removeClass("hilight");
}
});
}).mousedown(function() {
$grumble.addClass("hilight");
});
Thanks for your interest. If there are any further problems please raise them as bugs on the github page. https://github.com/jamescryer/grumble.js
Use the grumble and button parameters on the onHide callback like this:
$('#grumble').grumble({
text: 'Whoaaa, this is a lot of text that i couldn\'t predict',
angle: 85,
distance: 50,
showAfter: 4000,
hideAfter: 2000,
onHide: function(grumble, button) {
grumble.bubble.remove();
grumble.text.remove();
button && button.remove();
}
});
This allows you to remove only the "trash" (I prefer "leftovers") associated with that specific tooltip/popup/bubble. Note that button only exists if hasHideButton is true, hence the button && existence check.
Why do you want to remove it? Is the 'trash' causing problems with browser performance?
In general, the only way to do this is to dig into the plugin source and add a function to remove the plugin, if one is not already present. If you just remove the related DOM elements you will leave behind references to them and events handlers that access them.
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.