Screen shot 2009-11-26 at 21.09.16If you’re making a form with today’s standards you often have to make several select elements with the options of the next select elements referring to the previous one. It’s a simple task to create a small Mootool which do it for you but it’s always a time consuming process, especially if you’ll have a lot of select boxes. I thought that if I have use this functionality more than once a class would be helpful.

This class links the select boxes to each other. When you change the first select element the options for the following select boxes are loaded. Simple as that.

View example Download file Download Zip

The html

Note that only the first select element has some options in it. These can be static html or generated by php.





The css

These css is only for the style of the form elements. Nothing special.

Do you need a neat little loading gif? Just go to Ajaxload and download your own!

.loading{
	background: url(images/loading.gif) 170px center no-repeat;
	}

.inputbox {
	width: 220px;
	padding: 5px;
	border:1px solid #EEE;
	font: 12px "Courier New", Courier, monospace;
	color: #333;
	}

select.inputbox {
	width: 232px;
	}

The javascript

This is where the magic happens. Two variables are very important to make this class work.

  • value: This is the name of the object that is the value in the future option element
  • option: This is the name of the object that is the visual value of in the option element
window.addEvent('domready', function() {
	var l = new cvLinkSelect({
        select_class: '.linked', //the class for the linked select boxes
		url: 'yourajaxurl.php', //pad to ajax file
		multiple_var: true, //use multiple get variables or only one
		loadClass: 'loading', //the css class that is added when a request is made
		isLast: function(){
			alert('Last dynamic selectbox!');
		},
		hasnoSub: function(){
			alert('This option has no depending options. Lets hide the next elements!');

			l.select.each(function(s){
				if(l.select.indexOf(s) >= l.select.indexOf(l.current)){ //if the select depends on the parent
					s.hide();
				}
			});
		},
		hasSub: function(){
			l.select.each(function(s){
				if(l.select.indexOf(s) >= l.select.indexOf(l.current)){ //if the select depends on the parent
					s.show();
				}
			});
		}
	});
});

The JSON format

You have to make sure that the AJAX request returns a JSON format that is formatted like the example below.

NOTE
Be sure that the json always contains a value and a html object. See the demo for a example.

[{"value":"wheels","html":"Wheels"},
{"value":"body","html":"Body"},
{"value":"exaust","html":"Exaust"}]

The php

The php file has to return a JSON formatted string depending on the selected value. You can use if-statements or use a switch method to print the exact output but the basics are just this:

if(isset($_GET['category'])){
	echo '[{"value":"wheels","html":"Wheels"},{"value":"body","html":"Body"}]';
}elseif(isset($_GET['type'])){
	echo '[{"value":15,"html":"15 inch"},{"value":17,"html":"17 inch"}]';
}

When your website is dynamic you probably prefer to use a flexible way of presenting the JSON variable. Read more about this in another article I’ve wrote. I think it’s very useful so read it and don’t reinvent the wheel.

Multiple GET variables

In the old version we had only request with this kind of urls:

ajax.php?category=var

But in my opinion this wasn’t sufficient for the main goal: Get the right data in the select boxes and make programming easier. So that’s why I’ve made an update to the class so you can get this kind of requests:

ajax.php?category=var&type=var2

These urls can be activated via the multiple_var option in the class. Before you use them: please make sure that your php script has the correct architecture. A very basic example:

if(!empty($_GET['category'])){
	if($_GET['category'] == 'car'){
		if(!empty($_GET['type'])){
			if($_GET['type'] == 'wheels'){
				/* return the wheels JSON variabele */
			}
		}else{
			/* return the car JSON variable */
		}
	}
}

I hope you enjoy this class and if you have any questions or suggestions; please leave a comment!

Changelog

  • v1.4 30 mrt 2010
    • Fixed the weird Elements bug in Mootools 1.2.4
    • Upgraded the speed with document.createDocumentFragment()
  • V1.3 26 feb 2010
    • It’s possible to disable the select elements when no options are available. This is great feedback
  • V1.2.2 – 21 feb 2010
    • Fixed the reset bug. It will be resetting all the select elements if an empty value is set.
  • V1.2.1 – 20 feb 2010
    • Reset the depending select elements if when a user changes the upper select element
  • V1.2 – 26 nov 2009
    • Maintain an empty element with a short discription and an empty value in your html page and the class will  not remove this one while you change the parent select box
    • Set the value of the first select box to the empty option after page reload
    • Comments added for developers and curious people
    • Updated the example
    • Removed some overhead in the class
  • V1.1 – 2 nov 2009
    • Multiple GET variables in the request url.
    • If the request is empty it’s the last item in the linked selectboxes range. In this case we have to fire the isLast function.
  • V1.0 – 1 nov 2009
    • First release of the link select class

Related Posts

Comments (31)

avatar

Crispijn says:

Enjoy this class everyone! This was very useful for me developing several projects I’m working on!

Fiorio says:

GOOOOOOOOOOOOOOOOODDDDDDDDDDDDD !!!!!
This class is beautiful and useful ! thx for it !

avatar
avatar

Me says:

In my php File, i put
print ‘[{"value":"wheels","html":"Wheels"}, {"value":"body","html":"Body"},
{"value":"exaust","html":"Exaust"}]‘;
for test, and il don’t work !
Why please ?

Crispijn says:

I’m working on a zipfile with a working example. I’ll post this zipfile asap!

avatar
avatar

Me says:

it’s ok…. i’ve worked with a version of mootools which didn’t work with…
i take yours and it works perfectly…
You Build mootools with which component ?

Crispijn says:

The zipfile is ready! Check out the links on the top of the post!

Enjoy!

avatar
avatar

zope says:

Very Very nice Job ! thanks for sharing.

Is it possible to use a data pair like {“hard”,”Hard”} instead of {“value”:”hard”,”html”:”Hard”} ?
I think that makes the PHP-file a bit compact.

Crispijn says:

@Zope:

I’ve tried to find a valid JSON structure to work with the JSON file as you requested, it is indeed much cleaner. But I think that it isn’t possible. In a JSON array you’re still working with objects and You’ll always need a ‘key’ to point to the right thingy. Check out some examples on Adobe Labs. The Examples are working with Spry’s and we don’t but you’ll get the point.

http://labs.adobe.com/technologies/spry/samples/data_region/JSONDataSetSample.html

I’d like to implement your suggestion so If you’ve other suggestions or sources about JSON I’ll keep myself recommended.

avatar
avatar

zope says:

Hi Crispijn,

For example select: Car / Body / Red

Now If I change Selectbox1 to — Select the category –

Selectbox2 and Selectbox3 stay unchanged on ( Body / Red ).
Is there a solution for it, may be a local onChange-reset or delete, without sending the data to server ?

In case of the JSON-File, I’m trying to find a solution.
It may be possible to use the php “json_encode” function.

Crispijn says:

You’re right. Just add an else after the if-statement on line 34 in cvLinkSelect.js.

if(el.value != ”){ //if the value is not empty get request
self.getStatus(el);
}else{ //remove options for all the following select elements
self.removeOptions();
}

The JSON format is not very coplex but it’s the javascript that is not that flexible.

$arr = array( array(15 => ’15 inch’),
array(17 => ’17 inch’),
array(19 => ’19 inch’)
);

echo json_encode($arr);

Does:

[{"15":"15 inch"},{"17":"17 inch"},{"19":"19 inch"}]

So that’s exactly what we’d prefer but how to manage this with javascript? I’m not sure this will work.

avatar
avatar

Crispijn says:

I’ve created an php function that maybe is the answer for our question. In most cases you’ll get an array returned from the database. What do you think of this?

$arr = array(//array(id,name),
array(15,’15 inch’),
array(17,’17 inch’),
array(19,’19 inch’)
);

function json_cvLinkSelect($arr = array()){
$json = array();

foreach($arr as $ob){
$keys = array(‘value’,'html’);

array_push($json,array_combine($keys,$ob));
}

return json_encode($json);
}

echo json_cvLinkSelect($arr);

Returns exactly what we’d like: [{"value":15,"html":"15 inch"},{"value":17,"html":"17 inch"},{"value":19,"html":"19 inch"}]

zope says:

Hi Crispijn,

Thanks ! It works now.

btw. believe it or not, it works even on IE6 ;) … that’s amazing
The css and the loading-spinner fails, but the script works.

With 6 selectboxes, You can imagine how complicated the JSON file will be. But I will implement the JSON structure as it is.

Thanks again for this great script!

avatar
avatar

Crispijn says:

No problem, I’m happy that you enjoy it so much! On which website are you implementing this class? I’m curious where the class is implemented!

I’ll update the class, the example and the download file.

Cheers!

zope says:

Hi Crispijn,

I think your approach makes the JSON file smaller, but at the same time, the server response time will be longer. (save data in arrays / split array data / join data / create a new array / … etc…)

Your original JSON file will be bigger, but have faster server response time. (Just compare a single variable-value and echo the data ). seems easier to me.

avatar
avatar

Crispijn says:

Yeah, it’s slower but I don’t think you can mention that extra time. I think you haven’t compared the request time on both workarounds and neither do I but I think it is negligible and how to make this output dynamical? I don’t think there is a shorter and better answer for it.

But have you downloaded the ZIP file with the working example? The structure has not changed at all, just the way it is presented has changed.

So there are two options. If you do have a static website just use the function I’ve wrote to make your JSON file.
If you have a dynamic website the function I’ve written is the answer for you!

zope says:

Hi Crispijn,

I tested the new-JSON file. It’s easy to handle. But I have to test it with a big JSON file to see the difference.

I go back to my previous question.
The selection of — Select the category — on selectbox1
resets only selectbox3 …. but not the selectbox2.

avatar
avatar

Crispijn says:

I’ve solved the bug. Glad you noticed it. Is it like you wish now? I’m thinking of disabled select elements if the previous value isn’t set. It’s great feedback for the user.

What do you think?

zope says:

Hi Crispijn,

yes. That’s perfect !
I find both ways very good. (Reseting or disabling the SB)

Thanks !

avatar
avatar

Crispijn says:

I’ve released v1.3 a couple of days ago. I hope you’ll enjoy it!

ghazal says:

hello,
nice stuff but you got an error in your class which prevents it to work with moo 1.2.4 core.
—>
should be :
var cvLinkSelect = new Class({
Implements: [Options,Events],
etc…
Otherwise, Firebug says that cvLinkSelect is not a contructor.
Hope it helps.

avatar
avatar

Crispijn says:

Er, what did you change then? I couldn’t find a difference?

ghazal says:

Hi,
sorry, I was in a hurry.
I always check your kind of script with regular and last core and more scripts (dlded full from mootools.net).
cvLinkSelect class was not working within this context and written this way :
var cvLinkSelect = new Class({
Implements: [Options,Element,Events],
It got the previously mentioned Firebug error :
** cvLinkSelect is not a contructor
As I could not see any major bug in the code, I checked with this :
var cvLinkSelect = new Class({
Implements: [Options,Elements,Events],
So so results.
Getting rid of “Element(s)” did the trick altogether.
Don’t ask me why !
And again, don’t get me wrong, this is a very useful script, and I will definitely use it.

avatar
avatar

Crispijn says:

Ohw! I’ll investigate why this conflicts with Mootools 1.2.4 and I’ll update the class and the documentation!

Thanks!

zope says:

A lightweight tool. So small and so powerful.
Great package.

thank you again

avatar
avatar

zope says:

@ghazal
The removal of “Element” makes it compatible with Moo-1.2.4 ….
but at the same time the script doesn’t reset, if an empty value is selected.

ghazal says:

Well,
thank you for the explanation.
We will have to find a solution. I’ll look into it and get back to you if I find it. Not obvious, though …

avatar
avatar

Crispijn says:

I’ve fixed the bug. I don’t know how but I’ve changed some things in the class and everything is working fine now. If you do have problems, please let me know. I’ll look after it.

Also the class has been upgraded, it’s faster than it used to be. If you want to know what I’ve changed read my latest article about createDocumentFragment(), http://youngdutchdesign.com/speeding-up-mootools-with-createdocumentfragment.

Hope you’ll enjoy it!

zope says:

Hi Crispijn,

Selecting an empty-value on selectbox-1 is the same as
reseting the form by removing the dynamic DOM elements.
(That means all injected JSON arrays will be removed from all dynamic select-boxes )

Is it possible to simulate the same effect with a Reset-button ?

avatar
avatar

Crispijn says:

That could be faster indeed. I’ll look to this feature this week.

Thanks for your suggestion!

zope says:

Hi Crispijn,

I made the following changes:

I removed the overhead (value: and html:) from the PHP file.
I changed the “array of Hashes” to an “array of Arrays”

old:

echo ‘[
{"value":"wheels","html":"Wheels"},
{"value":"body","html":"Body"}
]‘;

new:

echo ‘[
["wheels","Wheels"],
["body",""Body"]
]’;

—————

The crossponding changes in cvLinkSelect class is

old:

var option = new Element(‘option’,{‘value’: o.value, ‘html’: o.html});

new:
var option = new Element(‘option’,{‘value’: o[0], ‘html’: o[1]});

I also added this line as a header on the PHP file to get a JSON response instead of HTML response.

header(‘Content-Type: application/json; charset=utf-8′);

Cheers!

avatar
avatar

Crispijn says:

Great one! I didn’t thought about that! Thanks for sharing!

Crispijn

Leave a Comment

Get your own Gravatar!
Your email will never be published!

Notify me of followup comments via e-mail

Top of Page