Chained AJAX Selects

Chained AJAX Selects

In today’s tutorial, we will build a set of chained select elements. Selecting an option in one of them will trigger an update on the page, showing you more choices to refine your selection. We will describe the options server side with PHP, so it is easy for you to hook today’s example to a database.

The idea for this tutorial was suggested by Casper Hansen from Denmark.

The HTML

As you can see from the screenshot below, the select box is accompanied by a title that explains what the selection is about. The title and the selectbox are enclosed in a LI item.

Chained AJAX Selects with jQuery and PHP

Chained AJAX Selects with jQuery and PHP

When adding more questions, additional LIs are created by jQuery. All of these sit inside an unordered list called #questions. The title and options for these items are served as JSON, as you will see in the PHP part of the tut. Here is the markup that is generated for the li items:

index.html – generated code

<ul id="questions">
	<!-- Generated by jQuery -->
	<li>
		<p>What would you like to purchase?</p>
		<select data-placeholder="Choose a product category">
			<option data-connection="phoneSelect" value="Phones">Phones</option>
			<option data-connection="notebookSelect" value="Notebooks">Notebooks</option>
			<option data-connection="tabletSelect" value="Tablets">Tablets</option>
		</select>
	</li>
	<!-- The next sections are inserted here depending on the choices above -->
</ul>

You might notice in the demo page that we aren’t using the default browser select controls. This is because we are making use of the Chosen jQuery plugin to upgrade our selects into the fancy widgets you see. We simply need to call the chosen() method on the selects, and the plugin will handle the rest.

The jQuery code

Here is what our jQuery code does in short – it fetches the select boxes information as JSON from the server, generates their HTML, and sets up event listeners for selection changes. If a change in the selection does occur, the process is repeated for the new select item.

In the code, this is achieved using two JavaScript functions:

  • refreshSelects triggers the Chosen plugin and binds event listeners every time an item is added to the page;
  • fetchSelect requests a JSON feed from the server and generates the markup from the response.

You can see them below.

assets/js/script.js

$(function(){

	var questions = $('#questions');

	function refreshSelects(){
		var selects = questions.find('select');

		// Improve the selects with the Chose plugin
		selects.chosen();

		// Listen for changes
		selects.unbind('change').bind('change',function(){

			// The selected option
			var selected = $(this).find('option').eq(this.selectedIndex);
			// Look up the data-connection attribute
			var connection = selected.data('connection');

			// Removing the li containers that follow (if any)
			selected.closest('#questions li').nextAll().remove();

			if(connection){
				fetchSelect(connection);
			}

		});
	}

	var working = false;

	function fetchSelect(val){

		if(working){
			return false;
		}
		working = true;

		$.getJSON('ajax.php',{key:val},function(r){

			var connection, options = '';

			$.each(r.items,function(k,v){
				connection = '';
				if(v){
					connection = 'data-connection="'+v+'"';
				}

				options+= '<option value="'+k+'" '+connection+'>'+k+'</option>';
			});

			if(r.defaultText){

				// The chose plugin requires that we add an empty option
				// element if we want to display a "Please choose" text

				options = '<option></option>'+options;
			}

			// Building the markup for the select section

			$('<li>\
				<p>'+r.title+'</p>\
				<select data-placeholder="'+r.defaultText+'">\
					'+ options +'\
				</select>\
				<span class="divider"></span>\
			</li>').appendTo(questions);

			refreshSelects();

			working = false;
		});

	}

	$('#preloader').ajaxStart(function(){
		$(this).show();
	}).ajaxStop(function(){
		$(this).hide();
	});

	// Initially load the product select
	fetchSelect('productSelect');
});

Great! We are now left with generating the actual JSON feed. Notice that the fetchSelect function takes a string argument. This is the key we will be passing back to PHP, denoting which set of items we want.

Here is a sample response from our PHP script:

{
    "items": {
        "Phones": "phoneSelect",
        "Notebooks": "notebookSelect",
        "Tablets": ""
    },
    "title": "What would you like to purchase?",
    "defaultText": "Choose a product category"
}

fetchSelect loops through the items and uses the keys as content of the option elements, and the values as connections. Phones and Notebooks would cause the script to generate new select boxes, while Tablets would not.

Improved Select boxes using the Chosen Plugin

Improved Select boxes using the Chosen Plugin

The PHP

We need to somehow store the information about the select boxes, the options they contain and the connections between them. With a database this could be done by selecting a specific set of rows. But here we will be storing this data statically as objects. For this purpose, we will define a simple class that will hold the information for a select box:

ajax.php / 1

// Each select box will be an instance of this class

class SelectBox{
	public $items = array();
	public $defaultText = '';
	public $title = '';

	public function __construct($title, $default){
		$this->defaultText = $default;
		$this->title = $title;
	}

	public function addItem($name, $connection = NULL){
		$this->items[$name] = $connection;
		return $this;
	}

	public function toJSON(){
		return json_encode($this);
	}
}

Now we only need to create an instance of this class for every select box, and call the addItem() to add options. This method has an optional $connection parameter, that holds the name of a dependent select box.

ajax.php / 2

/* Configuring the selectboxes */

// Product selectbox

$productSelect = new SelectBox('What would you like to purchase?','Choose a product category');
$productSelect->addItem('Phones','phoneSelect')
			  ->addItem('Notebooks','notebookSelect')
			  ->addItem('Tablets','tabletSelect');

// Phone types

$phoneSelect = new SelectBox('What kind of phone are you interested in?', 'Pick a phone type');
$phoneSelect->addItem('Smartphones','smartphoneSelect')
			->addItem('Feature phones','featurephoneSelect');

// Smartphones

$smartphoneSelect = new SelectBox('Which is your desired smartphone?','Choose a smartphone model');
$smartphoneSelect->addItem('Samsung Galaxy Nexus')
				 ->addItem('iPhone 4S','iphoneSelect')
				 ->addItem('Samsung Galaxy S2')
				 ->addItem('HTC Sensation');

// Feature phones

$featurephoneSelect = new SelectBox('Which is your desired featurephone?','Choose a feature phone');
$featurephoneSelect->addItem('Nokia N34')
				   ->addItem('Sony Ericsson 334')
				   ->addItem('Motorola');

// iPhone colors

$iphoneSelect = new SelectBox('What color would you like?','Choose a color');
$iphoneSelect->addItem('White')->addItem('Black');

// Notebook select

$notebookSelect = new SelectBox('Which notebook would you like to buy?', 'Choose a notebook model');
$notebookSelect->addItem('Asus Zenbook','caseSelect')
			   ->addItem('Macbook Air','caseSelect')
			   ->addItem('Acer Aspire','caseSelect')
			   ->addItem('Lenovo Thinkpad','caseSelect')
			   ->addItem('Dell Inspiron','caseSelect');

// Tablet select

$tabletSelect = new SelectBox('Which tablet would you like to buy?', 'Pick a tablet');
$tabletSelect->addItem('Asus Transformer','caseSelect')
			 ->addItem('Samsung Galaxy Tab','caseSelect')
			 ->addItem('iPad 16GB','caseSelect')
			 ->addItem('iPad 32GB','caseSelect')
			 ->addItem('Acer Iconia Tab','caseSelect');

// Case select

$caseSelect = new SelectBox('Buy protective casing?','');
$caseSelect->addItem('Yes')->addItem('No');

// Register all the select items in an array

$selects = array(
	'productSelect'			=> $productSelect,
	'phoneSelect'			=> $phoneSelect,
	'smartphoneSelect'		=> $smartphoneSelect,
	'featurephoneSelect'	=> $featurephoneSelect,
	'iphoneSelect'			=> $iphoneSelect,
	'notebookSelect'		=> $notebookSelect,
	'tabletSelect'			=> $tabletSelect,
	'caseSelect'			=> $caseSelect
);

The code above defines a number of select items and places them in the $selects array. When this script receives an AJAX request, it will look into this array and return a response:

ajax.php / 3

// We look up this array and return a select object depending
// on the $_GET['key'] parameter passed by jQuery

// You can modify it to select results from a database instead

if(array_key_exists($_GET['key'],$selects)){
	header('Content-type: application/json');
	echo $selects[$_GET['key']]->toJSON();
}
else{
	header("HTTP/1.0 404 Not Found");
	header('Status: 404 Not Found');
}

By calling the toJSON() method we defined in the beginning, we output all the data for the select object as JSON, ready for use by our jQuery frontend.

With this our Chained AJAX Selects example is complete!

Done

You can use this example to power user guides, product recommendations or search pages. Upgrading the script to use a live database is straightforward and it will actually simplify the PHP script.

Join our newsletter and get our PSDs!18,875 people learn about HTML5, JS and more. Join them!

by Martin Angelov

Martin is a web developer with an eye for design from Bulgaria. He founded Tutorialzine in 2009 and it still is his favorite side project.

39 Comments

  1. Nice. I needed something like this a while back ago and I am sure I will use it soon!
    Thank you!

  2. Bdliet says:

    nice! thanks Martin!

  3. Георги says:

    Angelov, thanks for the tuts, also I'm proud to see bulgarian last name :) You are great person!

  4. Gürkan says:

    Tank you nice jop

  5. Chris says:

    Nice idea, makes the page much better looking

  6. Jan says:

    nice one, reminds me the one i send you via mail :)

  7. Martin Angelov says:

    Thank you for the awesome comments, folks!

  8. krypto says:

    uhhh, I already thought you abounded this blog! :) Another great tut! :)

  9. Deepak says:

    This is very good. Had developed it in Java, and now I myself was going to try this in PHP. You saved my day and I am sure I couldn't have done this better.

    Thanks.

  10. DevOps says:

    Hi Martin,

    I think choosing "chosen" was a good idea, but your thinking behind the UI Concept of Chained selects is flawed. It would make sense to extend chosen, instead of naively appending another selectbox with the next option. I mean even a simple input box with facetted tags and auto-suggestion (using phonetic similarity) would give better results.

    But good to hear from you. It's been silent. Did you work on a big freelance project?

  11. I'm enjoying playing with this script. Very cool.
    Am learning jQuery, json, etc.

    Where in the code would I insert the URL that a page would direct to when a user selects an item?

  12. Aneek says:

    Hello Martin,

    This type of Dependent Drop down is best chosen for the e commerce websites. An example would be choosing the Country->State->City.
    Really, this one is awesome. I was searching this filterable drop down for some time. Anyway, at last found it.

    I will try to make it in drupal for a commerce solution. I will comment here to let you know how I progressed.

  13. James says:

    HI Martin,

    Thank you so much for yet another fantastic tutorial. I am afraid I am genuinely having trouble "hooking it up" to a database and I think I may well have the wrong idea on how to do it.

    So far I have created a populateList() method in the SelectBox class. Within that I do a mysqyl_fetch_array() on my db and then I call the addItem() method within the subsequent "while" loop.

    My thinking was that this would populate the select items from my db but all it does it generate the FIRST item from the db only.

    Any tips from you or anyone else would be very much appreciated!!!

    1. Martin Angelov says:

      As long as you keep the same JSON format as its output, you should not have trouble with hooking the script to a database. You will need to use the key argument that is sent with the AJAX request to filter the results (be sure to escape it!).

  14. James says:

    For clarity, here is my method (sorry forgot to mention the little sort function)

    public function populateList() {

    $query = "SELECT * FROM `Database` WHERE member='Y'";

    $result = mysql_query($query) or die(mysql_error());

    $X = array();

    while($row = mysql_fetch_array($result)){

    $X[] = $row['name'];

    }

    sort($X);

    foreach($X as $member){

    return $this->addItem($member);

    }

    }

  15. Alberto Restifo says:

    Well, what to say?

    It is fantastic.

  16. Nirmal Shah says:

    Thank you for this flabbergasting tutorial Martin, but I'd request you to write a short tut on how to create the fading line in Photoshop. Please ?

  17. David says:

    Hey Martin,
    Great job! Will definitely use this in the next project. Thanks a ton!

  18. tonev says:

    I liked this tutorial but still cant figure why can't i hook it up with my database so it can grab results from there... when i try it the box just disappears...

  19. Jason says:

    Probably a noob question but how do you get the selected value off this? There's no name attribute at all..

  20. Poon-World says:

    I concur with Jason, I just finished spending a few hours trying to figure out how to "translate" the state of the selections to be able to process them in PHP and/or to a database.

    Maybe a short addon note to your tutorial would help to resolve this mystery for the ones of us who have this problem, that would be great.

  21. carbon says:

    I must agree with others above. I'm pretty new to mysql/php but even more of a noob when it comes to JSON / AJAX. This functionality is exactly what I'm looking for but I need all of the options to be dynamically populated.

    Anybody know of an explanation online that would point me in the right direction?

    Thanks for the help and great tutorial!

  22. Roland says:

    At the end of the questions it will be an answer. For example "Buy protective casing?" -> "Yes". But how can I link to a case (for example to an online shop url)?

    1. Joran says:

      Same question here... I'm trying right now, let you know when i find a asolution ;)

      1. Roland says:

        Oh, that would be great! Thanks a lot in advance!

  23. Justin G says:

    The download files are not correct it is just a HTML page that says to download it from here...

  24. zeno says:

    Thanks for the code. How can we populate the drop down with database? And Out the many column (say 5) from the database, how to use only few (2) in drop down and display the remaining columns(3).

  25. Dam says:

    Hi,

    How to make redirection after finish the choose ?

  26. Billy says:

    Hi, is it possible for the outcome to be a couple of check box instead of constantly coming out a drop box? Example level 1 drop box -> level 2 drop box -> level 3 check boxes.

  27. idpweb says:

    Is is possible to do the following:

    1. Have each subesquent question replace the last instead of appearing beneath it?
    2. Redirect the user to a URL after completion of the question sequence?

    Thanks!

    1. Jason 2 says:

      For "replacing" the box with the next question, try z-index and absolute positioning. Just put the new box on top. However, this is a bad move from a UI standpoint. What happens when the user wants to change their choice?

      Redirecting: Wrap in a <form> tag, set "action" to your new page, use a submit button to send the data.

  28. Tristan says:

    That's so cool,martin.i shall use it somewhere.

  29. metinuk says:

    Hi Martin,

    your tutorials are among the very best! I wish you could post more.

    Is there any way to lead to an url with the last selection? Thanks.

  30. pzp says:

    Hi Martin, hi all,

    First of all, thanks for this great scipt!

    I've also been trying to implement it on my website : I need my lists to be populated form SELECT queries to my mySQL database.

    I've paid a lot attention to what James said here
    http://tutorialzine.com/2011/11/chained-ajax-selects-jquery/#comment-20379
    and here
    http://tutorialzine.com/2011/11/chained-ajax-selects-jquery/#comment-20378

    However, I still don't manage to go further than him : Only the fist result appears in the json output :

    {"items":{"my first item from a long list":"caseSelect"},"defaultText":"Pick a tablet","title":"Which tablet would you like to buy?"}

    I keep looking at his populateList() function ... and everything is here : while, foreach ... but still only one single result ;(

    You said (@James) "you should not have trouble" -> Dude, I'm deep in it ^^

  31. Yangora says:

    Hi Martin,
    I am so glad to have found this forum. Thank you very much for sharing these great ideas.

    My question as a very new in jquery and ajax is how can i get the plug in that will work with the above codes?

    I don't know the right choice to make. Thank you very much!

  32. Harshit Laddha says:

    Hey Martin , cool thing you created this , well can you help me with a little problem of mine , all i want to do is
    to be able to go to specific links on selecting specific category , how can i do that please help ,
    your help would be strongly appreciated
    i can also pay you if you want me to
    thanks in advance

  33. upaw says:

    Hi Martin, how can I position the next drop down list not below the first? What I have in mind is I need to have unique #questions. TIA

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links