Advanced radio button grouping with jQuery
I was asked to do something rather interesting with some radio button inputs the other day. I haven’t come across this particular problem before and google didn’t return anything helpful, so I decided to share what I came up with.
First, the scenario
We have a standard form with 4 radio button groups. Each group has 4 radio buttons labeled from “1” to “4”. Now, when the user selects “option 1” in “group 1”, all the remaining “option 1” items need to be disabled. To see an example in action, you can view the demo here.
The problem
When I first started, I thought that it’s simply a matter of giving each “option” (regardless of group) a common class name – then just disabling all other options with the same class name. That works, to a point, but what if the user changes his mind and selects another option ? Now I had to find all previously disabled options, re-enable them and start all over again.
The solution
Essentially what I came up with was a basic nested loop to handle setting and unsetting the relevant “disabled” attributes. To achieve this, first we assign all “option 1” a class of “number1”, “option 2” a class of “number2” and so on.
Next, we run a basic for loop, and go through each “number” class (i.e. number1 to number4). For each class, we call a function. Inside this function is another loop – this time iterating over each radio button assigned the current class name. Using this loop, we remove any “disabled” attributes which may have been assigned previously. We also find out which item in that group is currently selected (if any) – this is so we can run a second loop to disable all those options not currently selected. Confused ? Now might be a good time to go through the code
The code
$(function(){ // fire our code when a radio button has been selected $("input[type=radio]").change(function(){ var name = $(this).attr("name"); // get the current button's group name $("input[name="+name+"]").removeAttr("selected"); // first we deselect "all" radio buttons within the same group $(this).attr("selected","selected"); // make sure our currently "changed" button is selected for(i=1;i<=4;i++){ // go through the 4 radio classes processRadio(".radio"+i); } }); /** Loops through each item with same "class" name and disables/enables where appropriate **/ function processRadio(class){ var empty; var id = ""; var buttons = $(class); // read all buttons with the specified class name into a jQuery object buttons.each(function(){ // loop through each item var me = $(this); var isSelected = me.attr("selected"); // bool value, based on whether the "selected" attribute is present or not me.removeAttr("disabled"); // clear the disabled attribute if present me.siblings("label").removeClass("disabled"); // same with the associated label element if (isSelected != empty && isSelected != ""){ id = $(this).attr("id"); // set our tmp id var to our currently selected item } }); // make sure we have an id, otherwise we'll get an error if (id != empty && id != ""){ buttons.each(function(){ // loop through each radio button once again if ($(this).attr("id") != id){ // set the disabled attributes if id attribute doesn't match our tmp id var $(this).attr("disabled", "disabled").siblings("label").addClass("disabled"); } }); } } });
I’ve commented as best I could, which hopefully makes more sense than my rambling above. Once again, there is a working demo available for you to play with. Be sure to have a look at the markup as well, might clear up a few questions.
I strongly suspect that there’s a more efficient method for achieving the same result – so if you have a better suggestion, tweak or link please let me know – I’d greatly appreciate it.
Mobile App Development – Snake Guide for South Africa
I took my first foray into mobile app development recently. Using Origo IDE I was pleasantly surprised at how easy this particular framework makes development for the Symbian (Nokia) environment.
Including the initial learning curve, it took me all of 5 days to complete the app in question, and I’m quite pleased with the result, even if it is still a bit rough around the edges. Unfortunately, since Origo IDE is still a work in progress, some functionality is simply not available yet (such as calling the phone’s dialler, sending sms’s, use of the camera – though these API’s are apparently being added as we speak). So I’ll be adding to the app as and when the framework gets updated.
The app itself is a guide / e-book on snakes most commonly found throughout South Africa. It’s available as a free download, so please feel free to play around with it and give me some feedback – good or bad, it’s all a learning experience.
The app has been tested and works on all Nokia Symbian S60 5th edition handsets . (Including Nokia N97, N97 mini, 5800, 5530 and X6 handsets.)
The information, images and idea for the app were all sourced and supplied by Simon Botes.
Howto: Create custom thumbnails from Flutter image fields
If you’ve worked with Flutter / Fresh pages (wordpress plugin) before, you’ll know that you can use the supplied get_image() function to insert the relevant image field into your template. The problem however is that these images are generated automatically during the upload process and thus conform to the whatever dimensions are set for that field.
There are instances however where you’d need to display the same image but at different sizes, for example a widget vs ‘more info’ display. This is when the following function becomes quite handy.
The auto thumbnail function
You can insert this function straight into your functions.php, a usage example will follow below.
function jt_get_thumb($fieldName, $groupIndex=1, $fieldIndex=1, $readyForEIP=true, $params, $returnimg=true){ // use flutter's default function to get our field value (non thumbnailed image). $file = get($fieldName, $groupIndex, $fieldIndex, $readyForEIP); // if the field's empty, we can't continue, return an empty string if (empty($file)){ return ""; } // we want the base filename $file = str_replace(FLUTTER_FILES_URI, "", $file); // get the current image dimensions $jtSize = getimagesize(FLUTTER_FILES_PATH.$file); // break our params up into a useable array. $params = explode("&",$params); $i = 0; foreach($params as $p){ $tmp = explode("=", $p); $jtParams[$tmp[0]] = $tmp[1]; $i++; } // no point in continuing if the width or height params aren't set, or the original image's dimensions are smaller than the supplied params if ((!isset($jtParams["w"]) || !isset($jtParams["h"])) || ($jtSize[0] <= $jtParams["w"] && $jtSize[1] <= $jtParams["h"])){ return ($returnimg) ? "<img src='". FLUTTER_FILES_URI.$file ."' />" : FLUTTER_FILES_URI.$file; } //generate thumb using flutter's default method (phpThumb class) include_once(FLUTTER_PATH . "/thirdparty/phpthumb/phpthumb.class.php"); $phpThumb = new phpThumb(); $phpThumb->setSourceFilename(FLUTTER_FILES_PATH.$file); $md5_params = md5($file . $params); $create_md5_filename = 'jt_th_'.$md5_params."_".$file; $output_filename = FLUTTER_FILES_PATH.$create_md5_filename; $final_filename = FLUTTER_FILES_URI.$create_md5_filename; // experimental, uncomment to test : check if the thumbnail has already been created and use that instead // if (file_exists($output_filename)){ // $attr_params = "style='width:".$jtParams['w']."px; height:".$jtParams['h']."px;'"; // return ($returnimg) ? "<img src='$final_filename' $attr_params />" : $final_filename; // } foreach($jtParams as $key => $val){ $phpThumb->setParameter($key, $val); } if ($phpThumb->GenerateThumbnail()) { if ($phpThumb->RenderToFile($output_filename)) { $file = $final_filename; } $attr_params = ""; } else { // if the thumbnail generator fails for some reason, rather return the original image with a forced style attribute $file = FLUTTER_FILES_URI.$file; $attr_params = "style='width:".$jtParams['w']."px; height:".$jtParams['h']."px;'"; } return ($returnimg) ? "<img src='$file' $attr_params />" : $file; }
Pretty straightforward, the function accepts the standard flutter get() parameters with 2 extras added on to the end. The last two parameters are as follows :
- $params (string): a standard query string containing the various parameters for the thumbnail generator. Example – “w=400&h=200&zc=1”. The available parameters conform to flutter’s default image thumbnail parameters, see flutter’s documentation for a full reference.
- $returnimg (bool, default true): set to true to return a formatted img tag, whereas false will return just the full URI based path to the image.
Example Useage
// will output full img tag echo jt_get_thumb("news_img", 1, 1, false, "w=250&h=250&zc=1"); // build the image string up manually echo "<img src='" . jt_get_thumb("news_img",1,1,false,"w=250&h=250&zc=1",false) . "' class='newsImgClass' />";
Naturally, this function assumes that the field being queried points to a valid image file, you could of course flesh it out with some fail-safe checks if you’re that way inclined. Please feel free to post any improvements in the comments and I’ll see about adding them to the next version for everyone to use.
Simplifying Flutter duplicate groups and fields
Flutter or Fresh-pages is a really handy plugin for WordPress. With it, you can quite easily create custom write panels, fields and groups of information.
Having worked with this plugin quite extensively recently, I have encountered a few issues which have proved somewhat frustrating, dealing with duplicate groups or fields in particular. Flutter uses a counter based system in which to access duplicate groups. Simple enough when using a standard for loop and all the duplicates are in the correct order – but somewhat tricky to handle when say, deleting one of the duplicates.
I’ve since resorted to writing a function to make handling these duplicates and a number of associated problems whilst looping through them much, much simpler.
Which brings us to the following function – which you can paste straight into your theme’s functions.php file :
function jt_get_flutter_duplicates($field, $group){ if (is_string($group)){ $numfiles = getGroupDuplicates($group); $isgroup = true; } else { $numfiles = getFieldDuplicates($field, $group); $isgroup = false; } $return = false; if ($numfiles > 0){ $return = array(); $count = 1; $tmp = ""; $first = ""; $total = ($numfiles > 1) ? 100 : 1; while($count <= $total){ if ($isgroup){ $value = get($field, $count, 1); } else { $value = get($field, $group, $count); } if (empty($value) || $value == $first || $tmp == $value){ $count++; continue; } elseif ($count == 1){ $first = $value; } $tmp = $value; $return[] = ($isgroup) ? $count : $value; } } return (is_array($return) && count($return) > 0) ? $return : false; }
I’m not going to go into too much detail right now, but essentially what this function does is gets the required field or group duplicates, loops through them (up to a max of 100 iterations just to be safe) and places them into an array containing the correct index values (if a group) or an array of field values (if a field).
Group duplicates
Set the first parameter to the name of a field within the group which should contain a unique value (such as name, or caption). The second parameter can be any field within the required group.
$groupItems = jt_get_flutter_duplicates("news_image", "news_image"); if (is_array($groupItems) && count($groupItems) > 0){ foreach($groupItems as $i) { echo get_image("news_image",$i, 1, 1); echo "<br />" . get("news_image_caption",$i, 1, false); } }
Field Duplicates
Set the first parameter to the name of the field you want to retrieve. Set the second parameter to the group index you want to retrieve the field duplicates from. For instance, you could have 1 group which can be duplicated, containing 3 fields, one of which can be duplicated. When looping through the group duplicates, you’ll want another sub-loop to handle the duplicated field contents. The function will return an array of values which you can then use in a standard foreach loop.
$groupItems = jt_get_flutter_duplicates("office_title", "office_title"); if (is_array($groupItems) && count($groupItems) > 0){ foreach($groupItems as $i) { echo get("office_title",$i,1,false); $contacts = jt_get_flutter_duplicates("office_contact", $i); if (is_array($contacts) && count($contacts) > 0) { foreach($contacts as $contact){ echo "<br />Contact Person: " . $contact; } } } }
This is of course a quick fix and by no means infallible, so I’d love to hear if you have any improvements or a simpler way of dealing with this particular issue.
6 jQuery snippets you can use to manipulate select inputs
When it comes to manipulating the DOM, fewer elements are more tiresome than the good old select input. Fortunately for us, jQuery makes what was once a headache, a walk in the park.
Listed below are 6 snippets which should make manipulating those selects more pleasant than say, pulling your own teeth.
1. Getting the value of a selected option.
$('#selectList').val();
This couldn’t be simpler. Remember how before jQuery, you had to use selectedIndex and all those lovely javascript methods. I do, and I don’t miss it one bit.
2. Getting the text of a selected option.
$('#selectList :selected').text();
Similar in concept to the first snippet with one difference. Where the first example gives you the “value” of the selected option, this example gives you the actual text contained inside the option tag.
3. Getting the text/value of multiple selected options.
var foo = []; $('#multiple :selected').each(function(i, selected){ foo[i] = $(selected).text(); }); // to get the selected values, just use .val() - this returns a string or array foo = $('#multiple :selected').val();
Once again, the same concept as the first two examples, except we’re now using jQuery’s “each()” method to loop through all selected options in a multiple select list. Each value or text value is read into an array for later use.
4. Using selected options in conditional statements
switch ($('#selectList :selected').text()) { case 'First Option': //do something break; case 'Something Else': // do something else break; }
Much like example 2, we’re getting the text() value of a selected option, only this time we’re going to use it inside a switch statement.
5. Removing an option.
$("#selectList option[value='2']").remove();
Using an attribute filter, we can find and therefore manipulate specific options within a select list. In this example we’re telling jQuery to find the option with value=”2″ and then remove it from the select list. We can similarly add options to a select list as we’ll see in example 6.
6. Moving options from list A to list B.
$().ready(function() { $('#add').click(function() { return !$('#select1 option:selected').appendTo('#select2'); }); $('#remove').click(function() { return !$('#select2 option:selected').appendTo('#select1'); }); });
Originally posted by Jeremy Martin, here we have 2 select lists and 2 buttons. If you click the “add” button, we remove the selected option from select1 and add that same option to select2. The “remove” button just does things the opposite way around. Thanks to jQuery’s chaining capabilities, what was once a rather tricky undertaking with JS can now be done in 6 lines of code.
And there you go. That wasn’t so bad now was it ? If you have any other handy snippets, or you’ve found an easier way to do something already covered here, why not leave a comment and share the love!
Update:
Incidentally, a few hours after first publishing this post I came across the following “select element cheat sheet”. Technically speaking, it may be a little big to be called a cheat sheet, but the author has given a very thorough and practical guide which I’m definitely going to be making use of.
How to validate Date of Birth with jQuery
In a recent project I had to implement what’s commonly referred to as a Legal Drinking Age (LDA) page. Basically what needs to happen is that the user has to enter their date of birth and thus ‘confirm’ that they are of a legal drinking age for their respective country. You can view an example of just such a page here.
This brought me to an interesting requirement – the user needs to enter their birth of date (in this case via 3 select boxes), their age then needs to be calculated from the entered date and they are either granted, or denied access based on the result.
Interestingly enough, after having a quick look around I hadn’t been able to find any similar example, so I had to come up with something myself. The following code is my solution :
$(“#lda-form”).submit(function(){
var day = $(“#day”).val();
var month = $(“#month”).val();
var year = $(“#year”).val();
var age = 18;
var mydate = new Date();
mydate.setFullYear(year, month-1, day);
var currdate = new Date();
currdate.setFullYear(currdate.getFullYear() – age);
if ((currdate – mydate) < 0){
alert("Sorry, only persons over the age of " + age + " may enter this site");
return false;
}
return true;
});
[/sourcecode]
First, we get the relevant entered date values - the “day”, “month” and “year”. I’ve also added an “age” var so that this can be easily edited if necessary – the current value is set to 18.
Next we create a Date object and call it “mydate”. We then use the object’s “setFullYear” method to set the value of “mydate” to the user’s entered birth date. You can view more information about this method here. The only thing to notice here is that we have subtract 1 from the “month” value as it’s 0 indexed.
Now that we have the user’s birth date sorted, we then create another Date object and call it “currdate”. The default value for “currdate” is the current date. We then set “currdate” to whatever the current date is minus our “age” value – i.e. Today – 18 years. Once again, we use the “setFullYear” method to set the date. However, to get the current date in a useable format, we use the “getFullYear” method and THEN subtract 18 from the resulting value. So to do this we use “currdate.getFullYear() – age”.
Once we have the date of 18 years ago and the user’s birth date, both in the same format, it’s simply a matter of making sure that the required date minus the birth date isn’t greater than 0. If so, we output an alert to inform the user then return “false” to make sure that the form doesn’t get submitted.
Pretty straightforward.
Still alive and kicking
Wow, what a crazy 6 weeks I’ve just had. Probably the busiest, most stressful and yet most rewarding stretch of my entire working career.
With that said, this is just a quick note to tell anyone out there who actually cares that I’m still alive, still kicking and will resume posting to this fascinatingly entertaining and informative blog shortly.