├── README.markdown ├── datasupplier.htm ├── datasupplier.php ├── index.htm └── src ├── jquery.relatedselects.js └── jquery.relatedselects.min.js /README.markdown: -------------------------------------------------------------------------------- 1 | # jQuery Related Selects 2 | 3 | Demos @ [http://www.erichynds.com/examples/jquery-related-selects/](http://www.erichynds.com/examples/jquery-related-selects/) 4 | jQuery project page @ [http://plugins.jquery.com/project/related-selects](http://plugins.jquery.com/project/related-selects) 5 | Blog post/comments @ [http://www.erichynds.com/jquery/jquery-related-dependent-selects-plugin/](http://www.erichynds.com/jquery/jquery-related-dependent-selects-plugin/) 6 | 7 | jQuery Related Selects is a plugin that allows you to create any number of select boxes whose options are determined upon the selected value of another. 8 | You pass an array or object of select box names, and the select boxes will depend on each other in the order in which they are passed in. 9 | 10 | When a select box is changed an AJAX request is sent to the file specified in the onChangeLoad property, passing the selected value of each select box 11 | as parameters. The data must be returned in either JSON or HTML format. An 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /datasupplier.php: -------------------------------------------------------------------------------- 1 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Related Selects 6 | 7 | 8 | 9 | 12 | 13 | 73 | 74 | 75 | 76 | 77 | 78 |

jQuery Related Selects Plugin

79 | 80 | 84 | 85 |

Note that in examples 1-6, the file that supplies the data pauses for 1 second so you can see the loading messages.

86 | 87 |

Example 1

88 |

The state select is pre-populated, and the rest will populate based on preceding values. Each select is configured with a custom loading message, and the default option text is set to "Choose an Option".

89 |
90 | 95 | 96 | 99 | 100 | 103 | 104 | 107 |
108 | 109 |

Example 2

110 |

The state and county selects are pre-populated and legitimate values are selected. Since the 'County' select already has a selected option, 111 | the 'Town' select automatically populates. Individual selects do not have any custom settings. No defaultOptionText override.

112 |
113 | 118 | 119 | 123 | 124 | 127 | 128 | 131 |
132 | 133 | 134 |

Example 3

135 |

The state select is populated, and the HTML structure of the other three are completely empty when the page loads.

136 |
137 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
149 | 150 |

Example 4

151 |

Basic, just two selects.

152 |
153 | 158 | 159 | 162 |
163 | 164 |

Example 5

165 |

Using the onChange, onLoadingStart & onLoadingEnd callbacks.

166 |
167 | 172 | 173 | 176 | 177 |
178 |
179 | 180 |

Example 6

181 |

Using the DisableIfEmpty option & the onEmptyResult callback.

182 |
183 | 188 | 189 | 192 | 193 | 196 | 197 |
198 |
199 | 200 | 201 |

Example 7

202 |

Retreiving data in HTML format instead of JSON. This option gives you more control of the options, like the ability to add optgroups. View the data file.

203 |
204 | 208 | 209 | 212 |
213 | 214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /src/jquery.relatedselects.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Related Selects plug-in 1.1 3 | * 4 | * http://www.erichynds.com/jquery/jquery-related-dependent-selects-plugin/ 5 | * http://github.com/ehynds/jquery-related-selects 6 | * 7 | * Copyright (c) 2009 Eric Hynds 8 | * 9 | * Dual licensed under the MIT and GPL licenses: 10 | * http://www.opensource.org/licenses/mit-license.php 11 | * http://www.gnu.org/licenses/gpl.html 12 | */ 13 | 14 | (function($){ 15 | $.fn.relatedSelects = function(opts){ 16 | opts = $.extend({}, $.fn.relatedSelects.defaults, opts); 17 | 18 | return this.each(function(){ 19 | new RelatedSelect(this, opts); 20 | }); 21 | }; 22 | 23 | var RelatedSelect = function(context, opts) { 24 | var $context = $(context), selects = []; 25 | 26 | // if the selects option is an array convert it to an object 27 | if($.isArray(opts.selects)){ 28 | selectsToObj(); 29 | } 30 | 31 | // make array of select names 32 | $.each(opts.selects, function(){ 33 | selects.push(key); 34 | }); 35 | 36 | // cache the options where the value is empty for each select before processing occurs. 37 | saveDefaultOptionText(); 38 | 39 | // go through each select box & settings passed into options 40 | $.each(opts.selects, function(elem,o){ 41 | var $select = $context.find("select[name='" + elem + "']"), // jquery ref to this select box 42 | $next = next(elem), // the select box after this one 43 | selectedValue = $select.val(); // currently selected value 44 | 45 | // extend element-specific options 46 | // set the defaultOptionText to whatever was passed in or the option where value is blank. 47 | o = $.extend({ 48 | defaultOptionText: opts.defaultOptionText || $select.data('defaultOption') 49 | }, opts, o); 50 | 51 | // store the new default option text 52 | $select.data('defaultOption', o.defaultOptionText); 53 | 54 | // bind the change event 55 | $select.change(function(){ 56 | o.onChange.call($select); 57 | process( $select, $next, elem, o ); 58 | }); 59 | 60 | // if there is already a selected option in this select and the next one is already populated, skip this iteration 61 | if(selectedValue && selectedValue.length > 0 && isPopulated($next)){ 62 | return; 63 | } 64 | 65 | // process the select box upon page load 66 | process( $select, $next, elem, o ); 67 | }); 68 | 69 | function saveDefaultOptionText(){ 70 | var $select, text; 71 | 72 | for(var x=1, len=selects.length; x 0 && value !== o.loadingMessage && $next){ 88 | 89 | // reset all selects after this one 90 | resetAfter(elem); 91 | 92 | // populate the next select 93 | populate($select,$next,o); 94 | 95 | // otherwise, make all the selects after this one disabled and select the first option 96 | } else if($next){ 97 | resetAfter(elem); 98 | } 99 | } 100 | 101 | function populate($caller,$select,o){ 102 | var selectors = [], params; 103 | 104 | // build a selector for each select box in this context 105 | for(var x=0, len=selects.length; x' + o.loadingMessage + ''); 114 | 115 | // perform ajax request 116 | $.ajax({ 117 | beforeSend: function(){ o.onLoadingStart.call($select); }, 118 | complete: function(){ o.onLoadingEnd.call($select); }, 119 | dataType: o.dataType, 120 | data: params, 121 | url: o.onChangeLoad, 122 | success: function(data){ 123 | var html = [], defaultOptionText = $select.data('defaultOption'); 124 | 125 | // set the default option in the select. 126 | if(defaultOptionText.length > 0){ 127 | html.push(''); 128 | } 129 | 130 | // if the value returned from the ajax request is valid json and isn't empty 131 | if(o.dataType === 'json' && typeof data === 'object' && data){ 132 | 133 | // build the options 134 | $.each(data, function(i,item){ 135 | html.push(''); 136 | }); 137 | 138 | $select.html( html.join('') ).removeAttr('disabled'); 139 | 140 | // html datatype 141 | } else if(o.dataType === 'html' && $.trim(data).length > 0){ 142 | html.push($.trim(data)); 143 | $select.html( html.join('') ).removeAttr('disabled'); 144 | 145 | // if the response is invalid/empty, reset the default option and fire the onEmptyResult callback 146 | } else { 147 | $select.html( html.join('') ); 148 | if(!o.disableIfEmpty){ $select.removeAttr('disabled'); } 149 | o.onEmptyResult.call($caller); 150 | } 151 | } 152 | }); 153 | } 154 | 155 | function isPopulated($select){ 156 | var options = $select.find('option'); 157 | return (options.length === 0 || (options.length === 1 && options.filter(':first').attr('value').length === 0)) ? false : true; 158 | } 159 | 160 | function resetAfter(elem){ 161 | var thispos = getPosition(elem); 162 | for (var x=thispos+1, len=selects.length; x0&&e!==f.loadingMessage&&a){n(c);s(b,a,f)}else a&&n(c)}}function s(b,a,c){var f=[],e=[];e=0;for(var t=g.length;e'+c.loadingMessage+"");d.ajax({beforeSend:function(){c.onLoadingStart.call(a)},complete:function(){c.onLoadingEnd.call(a)},dataType:c.dataType,data:e,url:c.onChangeLoad,success:function(l){var i=[],o=a.data("defaultOption");o.length>0&&i.push('");if(c.dataType==="json"&&typeof l==="object"&&l){d.each(l,function(u,v){i.push('")});a.html(i.join("")).removeAttr("disabled")}else if(c.dataType==="html"&&d.trim(l).length>0){i.push(d.trim(l));a.html(i.join("")).removeAttr("disabled")}else{a.html(i.join(""));c.disableIfEmpty||a.removeAttr("disabled");c.onEmptyResult.call(b)}}})}function w(b){b=b.find("option");return b.length===0||b.length===1&&b.filter(":first").attr("value").length===0?false:true}function n(b){b=p(b)+1;for(var a=g.length;b0&&w(f)||m(c,f,b,a)})};d.fn.relatedSelects.defaults={selects:{},loadingMessage:"Loading, please wait...",disableIfEmpty:false,dataType:"json",onChangeLoad:"",onLoadingStart:function(){},onLoadingEnd:function(){},onChange:function(){},onEmptyResult:function(){}}})(jQuery); 14 | --------------------------------------------------------------------------------