Monday, December 08, 2008

use jQuery AJAX to create options in a dropdown menu

If you love to hate Javascript, that half-breed, amateur-magnet language, and every onload() and eval() you ever saw, then jQuery just rained on your parade. Now, with jQuery, so many things are elegant and easy - the opposite of everything you've ever known about Javascript.

Here's how to populate a select menu's option list (values and labels) with data retrieved from an AJAX request. The impatient may jump to a working demo to see if this is even what you want. Maybe you were searching for some sink cleaning product.

Materials:
  • 1 webpage that loads jQuery and makes an AJAX request (page.html)
  • 1 script that receives the AJAX request and answers it (script.php)
Unlike so many pages written in plain Javascript, which don't function or are missing content in browsers that do not execute Javascript (Lynx, Googlebot, people who dont want your crappy code heating up their CPU and turned it off in Preferences), jQuery's philosophy is that if your browser will execute Javascript, then the page will be better, but if not, then the page should "degrade gracefully" and still at least mostly work. So in this tutorial, the demo page is served with an initial select menu that should be good enough, in case the user doesn't run the script.

Here's the markup that we start with (page.html): A form with an input field, select menu and a button that will trigger our script. In this example, a user enters a zip code in the input box and clicks the button. That will trigger an AJAX request to another resource, sending the zip code and retrieving a bunch of shipping options, which will then magically fill the dropdown menu.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>jQuery ajax demo populating select dropdown menu</title>
</head>
<body>
<form>
Zip: <input name="zip" type="text" size="5" maxlength="10" id="zip" value="" /><br />
<select name='shipping_method' id="shipping_method">
<option value="0" selected="selected">Select Shipping Method</option>
<option value='FEDEX_2_DAY' >FedEx 2 Day</option>
<option value='FEDEX_EXPRESS_SAVER' >FedEx 3 Day</option>
<option value='INTERNATIONAL_PRIORITY' >FedEx International</option>
<option value='PRIORITY_OVERNIGHT' >FedEx Priority Overnight</option>
<option value='STANDARD_OVERNIGHT' >FedEx Standard Overnight</option>
</select>
<input id="getrates" type="button" value="Lookup Shipping Rates" /><br />
</form>
</body>
</html>


Notice that there are no Javascript functions strewn into the markup as tag attributes (no onclick(), no onmouseover()). That's because jQuery separates code for behavior from code for presentation. All the jQuery code will go in the "head." From high up there, it can hook into the DOM using only its patent pending "selectors."

So now let me show you what to add inside the "head" tag in order to load jQuery on the page, and then jQuery code to write that will do all the work.

First, a tangent: Normally, you would download the jQuery.js libraries from jQuery.com onto your own webserver, and serve them to your visitors from there with a "script src" tag. That's fine if you want to do it that way, but there's another option to direct visitors get those libs from Google instead. There are a lot of good reasons for offloading this job, so I leave it to you to read about it. In this example, that's what we're doing.

So, first, add this inside your head tag (before or after title tag) to get your visitor to load the jQuery libraries:

<script src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("jquery", "1");
google.load("jqueryui", "1.5.2");
</script>


Now the browser understands jQuery. All the rest of your jQuery code can come next, in separate script tags, still in head:

<script type="text/javascript">
// When the DOM is ready to have events hook into it...
$(document).ready(function() {

// when the DOM element with id="getrates" is clicked....
$("#getrates").click(function() {

// make that button you clicked disappear...
$(this).hide(); // opposite of show() // See jQueryUI for more info

// and issue an AJAX request to a PHP script in the same directory
// getJSON is a method that expects a JSON-encoded data structure to be returned
// there are other AJAX methods too. See http://docs.jquery.com/Ajax
$.getJSON("script.php", // 1st arg to getJSON is the URI of the script
{
zipcode: $("#zip").val(),
random: "noise"
},
// 2nd arg to getJSON is an array of key-value pairs to send
// to the script. As many as you want.
// left-side is the GET variable name as seen by the target
// script, right-side is the value that will be sent.
// the above 2 args will cause the AJAX script to be hit with
// the query string: ?zipcode=90019&random=noise
// assuming that the user typed "90019" into the
// element on this page with the id="zip"


// 3rd arg is the callback function for the AJAX response
// The script.php responds in a JSON format so jQuery can
// understand the data structure natively, without you
// writing awful parsing of your own:
function(j) {
// erase all OPTIONs from existing select menu on the page
$('#shipping_method options').remove();

// You will rebuild new options based on the JSON response...
var options = '<option value="">Choose Shipping Method</option>';
// "j" is the json object that was output by your PHP script
// it is the array of key-value pairs to turn
// into option value/labels...
for (var i = 0; i < j.length; i++)
{
options += '<option value="' +
j[i].optionValue + '">' +
j[i].optionDisplay +
'</option>';
}
// stick these new options in the existing select menu
$("#shipping_method").html(options);
// now your select menu is rebuilt with dynamic info
}
); // end getJSON
}); // end clicked button to trigger AJAX
}); // end document ready
</script>


That's it. When the user clicks the button, an AJAX request is sent to script.php, and the response is used to rebuild the shipping_method dropdown menu. Prices appear inside the options list before your very eyes. If you want to see a working demo, check here

You may also want to copy this, name it "script.php" and save it on your server in the same directory as the html page above. It outputs a canned JSON answer that the jQuery code will use to make the select options.

<?php
# script.php

$pretend_results = array('PRIORITY_OVERNIGHT' => 39.69,
'STANDARD_OVERNIGHT' => 48.45,
'FEDEX_2_DAY' => 19.75,
'FEDEX_EXPRESS_SAVER' => 15.75);

$haOptions = array();
foreach($pretend_results as $method => $cost)
{
$haOptions[] = array('optionValue' => $method, 'optionDisplay' => "$method $$cost");
}

# make JSON object that will populate select dropdown menu options

if(function_exists('json_encode'))
{
echo json_encode($haOptions); # this puts the php array in the funny javascript array/object
# format so you don't have to know how to translate manually
}

else
{
# some lame web hosts dont have a new version of PHP (5.2+) that includes json functions in core
# so here, I fake it for you so you will have a working demo:
echo '[{"optionValue":"PRIORITY_OVERNIGHT","optionDisplay":"PRIORITY_OVERNIGHT $39.69"},{"optionValue":"STANDARD_OVERNIGHT","optionDisplay":"STANDARD_OVERNIGHT $48.45"},{"optionValue":"FEDEX_2_DAY","optionDisplay":"FEDEX_2_DAY $19.75"},{"optionValue":"FEDEX_EXPRESS_SAVER","optionDisplay":"FEDEX_EXPRESS_SAVER $15.75"}]';
}

# this output is what the jQuery ajax request will receive and parse in its ajax callback function
exit;
?>


When pasting the above PHP script into your editor, if your server does not have the json_encode() function (PHP version < 5.2) then be careful to NOT let your editor (like pico) wrap the long line dummy JSON string with hard line breaks. Hard returns in that data structure will break the fragile thing and your AJAX callback function will not execute.

It is still Javascript, after all, what did you expect?