Today I had to fix a nasty cross-browser compatibility issue involving two HTML <select> elements that worked in the following way:
- ddlRegioni: a list of regions in Italy, with the chance to select "All Regions" (the default).
- ddlProvince: a list of all city provinces in Italy, grouped by region using <optgroups>., with the chance to select "All Provinces" (default).
The two select are linked using a simple, yet effective "filter" logic implemented using a small JQuery script that works in the following way: if the user selects a region in ddlRegioni, the ddlProvince hides all the <optgroups> that don't match the region name, leaving only one visible, so that the user will only be able to either pick a province in that region or "All Provinces".
Here's the full HTML code, splitted in 3 parts:
- ddlRegioni - AKA the "filtering" select:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<select id="ddlRegioni" name="ddlRegioni"> <option value="">Tutte le regioni</option> <option value="14">ABRUZZO</option> <option value="18">BASILICATA</option> <option value="19">CALABRIA</option> <option value="16">CAMPANIA</option> <option value="9">EMILIA ROMAGNA</option> <option value="7">FRIULI VENEZIA GIULIA</option> <option value="13">LAZIO</option> <option value="8">LIGURIA</option> <option value="3">LOMBARDIA</option> <option value="12">MARCHE</option> <option value="15">MOLISE</option> <option value="22">NESSUNA (ESTERA)</option> <option value="1">PIEMONTE</option> <option value="17">PUGLIA</option> <option value="21">SARDEGNA</option> <option value="20">SICILIA</option> <option value="10">TOSCANA</option> <option value="5">TRENTINO ALTO ADIGE</option> <option value="11">UMBRIA</option> <option value="2">VALLE AOSTA</option> <option value="6">VENETO</option> </select> |
- ddlProvince - AKA the "filtered" select:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<select id="ddlProvince" name="ddlProvince"> <option value="">Tutte le province</option> <optgroup label="ABRUZZO"> <option value="24">CH - CHIETI</option> <option value="42">AQ - L'AQUILA</option> <option value="66">PE - PESCARA</option> <option value="88">TE - TERAMO</option> </optgroup> <optgroup label="BASILICATA"> <option value="52">MT - MATERA</option> <option value="71">PZ - POTENZA</option> </optgroup> <optgroup label="CALABRIA"> <option value="23">CZ - CATANZARO</option> <option value="26">CS - COSENZA</option> <option value="28">KR - CROTONE</option> <option value="75">RC - REGGIO DI CALABRIA</option> <option value="101">VV - VIBO VALENTIA</option> </optgroup> <optgroup label="CAMPANIA"> <option value="8">AV - AVELLINO</option> <option value="11">BN - BENEVENTO</option> <option value="21">CE - CASERTA</option> <option value="56">NA - NAPOLI</option> <option value="81">SA - SALERNO</option> </optgroup> <optgroup label="EMILIA ROMAGNA"> <option value="14">BO - BOLOGNA</option> <option value="31">FE - FERRARA</option> <option value="34">FC - FORLI'-CESENA</option> <option value="55">MO - MODENA</option> <option value="62">PR - PARMA</option> <option value="67">PC - PIACENZA</option> <option value="74">RA - RAVENNA</option> <option value="76">RE - REGGIO NELL'EMILIA</option> <option value="78">RN - RIMINI</option> <option value="104">RS - SAN MARINO</option> </optgroup> <!-- rest of the optgroupts skipped - there are 20 in totals --> </select> |
- The JQuery script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var $sR = $("select#ddlRegioni"); var $sP = $("select#ddlProvince"); $(function () { function updateProvinceList() { var regName = $sR.find("option:selected").text(); if ($sR.val() > 0) { $sP.find("optgroup:not([label='" + regName + "'])").hide(); $sP.find("optgroup[label='" + regName + "']").show(); if ($sP.find("option:selected").not(":visible")) { $sP.val(""); } } else { $sP.find("optgroup").show(); } } $("select#ddlRegioni").on("change", function (e) { updateProvinceList(); }); }); |
The Problem
The above code works like a charm in Chrome and Firefox... but not on Internet Explorer: sadly, IE doesn't enforce such behaviour: the style rule (display:none) is applied to the <optgroup> element but it doesn't actually work, and the elements stay visible.
The Fix
I reviewed a couple of solutions and workaround on StackOverflow and other similar websites: most users managed to fix that using some external plugins (such as the Toggle Dropdown Options plugin), which wasn't a good workaround for me since I wanted to find a solution using only vanilla JQuery; there were also some guys who suggested to just use the remove() JQuery function instead of hide(), which wasn't ideal either in my scenario: I couldn't permanently remove the <optgroup> elements from the DOM since I would need them whenever the user chooses a different region.
Eventually I came out with the following workaround, which is fully compatible with Chrome, Firefox, IE and should also work with any other browser as well:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var $sR = $("select#ddlRegioni"); var $sP = $("select#ddlProvince"); var $sPclone = $sP.clone(); function updateProvinceList() { var regName = $sR.find("option:selected").text(); if ($sR.val() > 0) { $sP.find("optgroup").remove(); $sP.append($sPclone.find("optgroup[label='" + regName + "']").clone()); } else { $sP.find("optgroup").remove(); $sP.append($sPclone.find("optgroup").clone()); } } $("select#ddlRegioni").on("change", function (e) { updateProvinceList(); }); |
The script only uses JQuery (no external plugins): it basically creates a in-memory clone of the "filtered" HTML <select> element, which is then used to selectively pull the required <optgroup> and append it to the "original" <select>, after having purged it from other (previous) optgroups.
Conclusion
That's about it: I hope this post will be useful for those who are looking for a "pure JQuery" solution for this problem. Happy coding!
References
- Jquery hide/show optgroup base on main selector (StackOverflow)
- Hiding Options of a Select with JQuery (StackOverflow)
- Hiding Option Groups (OPTGROUPS) in Chrome and Internet Explorer with JQuery
- Cross-Browser JQuery hidden optgroup