jQuery populate plugin

We have all used them, <select> elements that are populated by selecting the value of another <select> element. I found myself writing this logic over and over again, so I decided to put together this small jQuery plugin as abstraction.

Options

Populate accepts two arguments. The actual data that needs to be injected into the DOM and optionally an object with options.

Property Type Description
exclude {string} Exclude <option> nodes from being removed, e.g. the first node which could act as placeholder.
select {string} Select an <option> node after populating the <select> element. This property will also accept a valid jQuery selector like :first: or :eq(1).
onPopulate {object} Callback that will be dispatched after populating the <select> element. The first argument will be an array with the actual option nodes, this means that you could easily write some logic to handle an empty result set. Also, the this context refers to the actual <select> element that is being populated.
$('#selector').populate({key:value} , {
    exclude: ':first',
    select: ':eq(2)',
    onPopulate: function (nodes) {}
});

Example

Let me show you a prime example of a dynamically populated <select>:

With Populate you can achieve this as such:

$('#categories').on('change', function () {	
    $.post('xhr.php', {cat: this.value}, function (resp) {
        $('#subcategories').populate(resp.data.subcats , {
            onPopulate: function (opts) {
                // Handle empty results
                if ( !opts.length ) {
                    $(this).attr('disabled', 'disabled').append('<option>no subcategories available</option>');
                }
            }
        });
    });
});

Implementation

First and foremost, include the plugin as such (or lazy-load it, which is actually better for performance):

<script src="jquery-1.9.0.min.js"></script>
<script src="jquery.populate.min.js"></script>

Prep your HTML markup:

<select name="categories" id="categories">
	<option value="">Main categories</option>
	<option value="a">Category A</option>
	<option value="b">Category B</option>
	<option value="c">Category C</option>
	<option value="d">Category D</option>
	<option value="e">Category E</option>
</select>
<select disabled="disabled" name="subcategories" id="subcategories">
	<option value="">Select a main category first</option>
</select>

Initiate the plugin like this:

// You most likely want to encapsulate the logic within an event handler or in a callback after fetching data from the server
$('#categories').on('change', function () {
    $('#subcategories').populate({key: value});
});

Demos

Pass object literal

$('#subcategories').populate({
	'key1': 'value1',
	'key2': 'value2'
});

Pass serialized string to build the options nodes with

$('#subcategories').populate('aa=Subcategory+AA&bb=Subcategory+BB&cc=Subcategory+CC');

Pass HTML fragment directly

$('#subcategories').populate('<option value="aa">Subcategory AA</option><option value="bb">Subcategory BB</option><option value="cc">Subcategory CC</option>');

Enable submit button onPopulate

$('#subcategories4').populate( subcats, {
	onPopulate: function () {
		// `this` is a reference to the target element
		$(this).next().removeAttr('disabled');
	}
});

Select a specific option node when populated

$('#subcategories5').populate( subcats, {
	select: ':eq(1)'
});

Exclude the first option node when populating

$('#subcategories6').populate( subcats, {
	exclude: ':first'
});

Handle empty result sets

$('#subcategories7').populate( subcats, {
	onPopulate: function (opts) {
		// Opts is a reference to the result set (always an array)
	   if ( !opts.length ) {
		   $(this).attr('disabled', 'disabled').append('<option>no subcategories available</option>');
	   }
	}
});

Handle JSON response via AJAX

$('#categories8').on('change', function () {
	$.post('xhr.php', {cat: this.value}, function (resp) {
		$('#subcategories8').populate(resp.data.subcats);
	});
});

Unit tests