Creating a Better Multiple Select with jQuery and PHP

It’s about time for another tutorial. I wanted to share some code I’m using for administering a WordPress theme, but the principle can be applied to anything concerning multiple select in web forms. If you’re like me and think the standard HTML multiple select box just isn’t good enough – read on.

In this tutorial I’ll explain how to extensively improve the standard multiple select box by using a jQuery script. The script transforms the dull and impractical select box into something much more user-friendly along with the ability to reorder the chosen elements. I also show how you can retain the order of the selected items after the form is submitted – something I couldn’t find a guide for out there in The Great Internet.

[line]

[iconbutton icon=”icon-cog” right_icon=true href=”http://cappelendesign.no/wp-content/uploads/code/multiselect/multiselect.php” value=”Demo”]

[line]

What’s so bad about the default HTML multiple select?

As you may know, a multiple select is created by adding the argument multiple="multiple" to a <select> element. With the multiple argument the dropdown box changes into a solid box with the options listed inside – and allows selecting multiple items by holding down CTRL while clicking. For people less familiar with web forms it’s not intuitive to know that you need to hold down CTRL while clicking and that if you click on an item without CTRL down the selection disappears. To make such a select box user-friendly you’d have to at the very least write a paragraph somewhere explaining how to use it. And if the select box contains quite a few items, you’d need to scroll up and down. (In my example, listing around 160 blog names on a WordPress multisite makes it quite long and complex). Also, you can’t change the order of the selected items. Nay, we can do better – much better.

Alright, I’m convinced. How can I make it better?

The script we will be using is called jquery-ui-multiselect. You can see demos and download the script here by clicking “Download” in the upper right corner. The zip comes with quite a few files, but you only need two files: the script itself ui.multiselect.js and the stylesheet ui.multiselect.css. If you want it translated into your language, open up the js file and write your version of the three strings at the very bottom.

This script is dependent on jQuery and jQuery UI, and needs a jQuery UI stylesheet (jQuery UI offers many different themes so you need to choose one). So we need to either download those scripts and stylesheets as well, or simply use a CDN service, such as Google CDN service. I’ve chosen to do the latter.

Alright, that’s enough preperation. Let’s get to the code.

The stylesheets

First we make sure we load all necessary stylesheets inside the <head> of our web page. Here I’ve added jQuery UI’s theme smoothness via Google CDN. I’ve saved a copy of ui.multiselect.css and placed it inside a folder named css.

<link rel='stylesheet' href='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/smoothness/jquery-ui.css' type='text/css' media='screen' />  
<link rel='stylesheet' href='css/ui.multiselect.css' type='text/css' media='screen' />

The scripts

Then we add the javascript files. We need jQuery, jQuery UI (both through CDN), and then the locally saved ui.multiselect.js inside a js folder – in that order.

<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script>
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>
<script type='text/javascript' src='js/ui.multiselect.js'></script>

We also need some initializing javascript code to actually use the script, but we’ll come to that later. Let’s start with creating the HTML form and the PHP to process the form first and see what we get.

The web form

For this tutorial’s and simplicity’s sake I’m defining a simple associative PHP array of the elements to go inside the multiple select box inside the php file. When the form is submitted, we’ll receive another PHP array containing the selected items, but as I don’t store it anywhere it will be reset on page reload. Normally you’d fetch and store the elements from and into a database or a global class or value – but that’s outside the topic in this tutorial.

Here is our basic form with an associative PHP array of possible (totally random) choices. Keep in mind that the form’s enctype need to be multipart/form-data in order to accept arrays when submitting, and the <select> element’s name need a [] after its name value. Otherwise you’d only be able to submit a single value.

<?php $items = array('1' => 'Apples', '3' => 'Carrots', '4' => 'Oranges', '7' => 'Raisins', '12' => 'Bananas', '14' => 'Pears', '15' => 'Chocolate', '18' => 'Grapes'); ?> 
<form enctype="multipart/form-data" id="myform" method="post" action="index.php">
	<select name="selectitems[]" id="selectitems" multiple="multiple" style="width: 200px; height: 100px;">
		<?php foreach ($items as $i => $v) { ?>
		<option value="<?php echo $i; ?>"<?php if (in_array($i, $selected)) : echo ' selected="selected"'; endif; ?>><?php echo $v; ?></option>  
		<?php } ?>
	</select>
	<br/>
	<input type="submit" name="save" value="Save" />
</form>

The code above loops through the $items array and for each item it checks if the item’s inside the $selected array. In that case we set the option as selected.

So now we’ve got a form, but no PHP code which processes it when the Save button is clicked. Let’s add the form processing above the form – I’ve added it right after the <body> tag.

<?php
$selected = array();
//form processing
if (isset($_POST['selectitems'])) {
	$selected = $_POST['selectitems'];
}
if (!empty($selected)) : print_r($selected); endif;
?>

The above code checks whether a form was submitted. If so it stores the submitted array of choices into $selected. The last line is for debugging: if $selected contains something then print its contents – this is just so we can see what exactly was submitted.

Now we’ve got a working form, which displays a default HTML multiple select box and stores the selected items into a PHP value when submitting the form.

As I was explaining above and what you see to the left it isn’t very user-friendly. The user has to scroll up and down to see all elements, and either know or be told to hold down CTRL when clicking for selecting multiple items. Also we can’t edit the order. What if the form submitter likes raisins better than apples?

It’s time to put the multiselect script to work on our select box.

Doing some magic and replacing the default multiple select with something way better

We’ve already loaded the necessary scripts and stylesheets (see above), so all we need to do is to initialize the script on document ready. You can add this piece of code in a js file that is included or do like me, add an <script> block in the header (after all script libraries).

jQuery(document).ready(function() {
	jQuery("#selectitems").multiselect();
});

If you refresh your page now, the select box should’ve been replaced with the jQuery UI multiselect. PS: Since this select box setup requires more space, I’ve changed the select’s width to 450px and height to 180px. If you look closer in your HTML document (Firebug for Firefox is king), the original select box is hidden but the multiselect script retains the sizes you set on it and applies it to the new box.

This looks great! You can now double-click or drag elements from the right into the left column, click the “-” to remove items, reorder the elements, add all or remove all with a single click, and even search for elements.

The form above works just fine. But I have one improvement to share.

Making the multiple select retain the order after form submit

If you look closer at the debug printout in the screenshot above, I’ve submitted the form with a rearranged order: raisins (ID 7), oranges (ID 4) and lastly apples (ID 1). But the order in the selected column is wrong – it’s been rearranged to follow the order of the $items array. So, we receive the right order on form submission, but it isn’t reflected in the select box. This can confuse the user thinking the reordering has gone bad, and force him or her to rearrange the items when changing the selection at a later time.

The solution is to modify the generation of the select box so we add the items in the selected array first and then add the other items excluding those which were added from the first array. In the first loop we loop through the $selected array and we set all items to be selected with selected="selected", and in the second loop we skip items which can be found in $selected, and we don’t bother to set any as selected.

In our form code we replace the select box with this.

<select name="selectitems[]" id="selectitems" multiple="multiple" style="width: 450px; height: 180px;">
	<?php //first we add the list of selected items if any
	foreach ($selected as $sel) { ?>
	<option value="<?php echo $sel; ?>" selected="selected"><?php echo $items[$sel]; ?></option>
	<?php } ?>
	<?php foreach ($items as $i => $v) { //then insert all items, skipping those who were added above 
		if (in_array($i, $selected)) : continue; endif; //skip ?>
	<option value="<?php echo $i; ?>"><?php echo $v; ?></option>
	<?php } ?>
</select>

And there we have it – a highly user-friendly form with the possibility of rearranging the items.

Demo and full example file

In case you missed the demo, click the button earlier in the post to see it in action. Here is the full index.php file I’ve used in case you get stuck on something.

<!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">
	<head profile="http://gmpg.org/xfn/11">
		<title>Multiselect</title>
		<link rel='stylesheet' href='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/smoothness/jquery-ui.css' type='text/css' media='screen' />  
		<link rel='stylesheet' href='css/ui.multiselect.css' type='text/css' media='screen' />
		
		<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script>
		<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>
		<script type='text/javascript' src='js/ui.multiselect.js'></script>
		<script type='text/javascript'>
			jQuery(document).ready(function() {
				jQuery("#selectitems").multiselect();
			});
		</script>
	</head>
	<body>
		<?php
		$items = array('1' => 'Apples', '3' => 'Carrots', '4' => 'Oranges', '7' => 'Raisins', '12' => 'Bananas', '14' => 'Pears', '15' => 'Chocolate', '18' => 'Grapes'); 
		$selected = array();
		
		//form processing
		if (isset($_POST['selectitems'])) {
			$selected = $_POST['selectitems'];
		}
		if (!empty($selected)) : print_r($selected); endif;
		?>
		
		<div id="content">
			<form enctype="multipart/form-data" id="myform" method="post" action="index.php">
				<select name="selectitems[]" id="selectitems" multiple="multiple" style="width: 450px; height: 180px;">
					<?php //first we add the list of selected items if any
					foreach ($selected as $sel) { ?>
					<option value="<?php echo $sel; ?>" selected="selected"><?php echo $items[$sel]; ?></option>
					<?php } ?>
					<?php foreach ($items as $i => $v) { //then insert all items, skipping those who were added above
						if (in_array($i, $selected)) : continue; endif; //skip ?>
					<option value="<?php echo $i; ?>"><?php echo $v; ?></option>
					<?php } ?>
				</select>
				<br/>
				<input type="submit" name="save" value="Save" />
			</form>
		</div>
	</body>
</html>