customA11ySelect.js Demo

Github Page

In 2016, a project I was working on for a major retail brand needed a customizable select dropdown. It needed to be fully accessible, have all the browser functionality of a normal form <select> dropdown, be capable of triggering any mobile web browser's native form <select> dropdown interface, allow multi-line text and images, look the same across all browsers, and provide a way for other functions to inject additional <option>s into the dropdown without a page refresh. I studied several existing plugins that already did this, including jQuery UI's Selectmenu, Greg Franko's SelectBoxIt, and Bootstrap's Selects, and created my own inspired by the best aspects of all of them — yet simpler, more customizable, more accessible, and with some additional features.

Example 1

Normal <select> element with 10 options. Pass an ID or class name to the .customA11ySelect() function, or just use select to enable the plugin for all <select> elements. While focused on the input, try hitting a letter on your keyboard to search for options. For example, hitting the 's' key will navigate through all options that start with 's', or typing 'mc' will go to 'McLaren' instead of 'Mercedes'. You can also use the arrow and tabs key to navigate through options, the spacebar and enter keys to select an option, and the escape key to close the dropdown when it's open (just like a normal <select> input).

<label for="f1-team">Formula 1 Teams:</label>
<select id="f1-team">
  <option value="ferrari" selected="selected">Scuderia Ferrari</option>
  <option value="mercedes">Mercedes-AMG Petronas</option>
  <option value="mclaren">McLaren Honda</option>
  <option value="redbull">Red Bull Racing</option>
  <option value="tororosso">Scuderia Toro Rosso</option>
  <option value="renault">Renault Sport</option>
  <option value="williams">Williams Martini Racing</option>
  <option value="forceindia">Sahara Force India</option>
  <option value="haas">Haas</option>
  <option value="sauber">Sauber</option>
</select>

<script>
  $(document).ready(function() {
    $('#f1-team').customA11ySelect();
  });
</script>

Example 2

Users can set a custom overflow height for the dropdown area (i.e.: the height of the dropdown that is visible as you scroll), by providing an overlay parameter to the JS function. By default, if the dropdown has more than 5 options, the dropdown's overflow height is 235px; if there are 5 or less options, it shrinks to the necessary height of those options. In the example below, a height of 400 is used to show more options at once.

<label for="f1-drivers">Formula 1 Drivers:</label>
<select id="f1-drivers">
    <option value="rai" selected="selected">Kimi Räikkönen</option>
    <option value="vet">Sebastian Vettel</option>
    <option value="ham">Lewis Hamilton</option>
    <option value="bot">Valtteri Bottas</option>
    <option value="ric">Daniel Ricciardo</option>
    <option value="ver">Max Verstappen</option>
    <option value="per">Sergio Perez</option>
    <option value="oco">Esteban Ocon</option>
    <option value="gro">Romain Grosjean</option>
    <option value="mag">Kevin Magnussen</option>
    <option value="van">Stoffel Vandoorne</option>
    <option value="alo">Fernando Alonso</option>
    <option value="hul">Nico Hülkenberg</option>
    <option value="pal">Jolyon Palmer</option>
    <option value="eri">Marcus Ericsson</option>
    <option value="weh">Pascal Wehrlein</option>
    <option value="kvy">Daniil Kvyat</option>
    <option value="sai">Carlos Sainz Jr.</option>
    <option value="mas">Felipe Massa</option>
    <option value="str">Lance Stroll</option>
</select>

<script>
    $(document).ready(function() {
        $('#f1-drivers').customA11ySelect({overlay:400});
    });
</script>

Example 3

Users can insert images into dropdown options by providing the image's relative path in a data-iconurl attribute on its respective <option> element (NOTE: images are only shown when the dropdown is open). This example also shows how the dropdown supports multi-line option text and text overflow with an ellipsis.

<div class="select-halfwidth">
    <label for="f1-circuits">Formula 1 Grand Prix Circuits:</label>
    <select id="f1-circuits">
        <option value="au" data-iconurl="img/AU.gif" selected="selected">Australian Grand Prix - Albert Park Circuit</option>
        <option value="cn" data-iconurl="img/CN.gif">Chinese Grand Prix - Shanghai International Circuit</option>
        <option value="bh" data-iconurl="img/BH.gif">Bahrain Grand Prix - Bahrain International Circuit</option>
        <option value="ru" data-iconurl="img/RU.gif">Russian Grand Prix - Sochi Autodrom</option>
        <option value="es" data-iconurl="img/ES.gif">Spanish Grand Prix - Circuit de Catalunya</option>
        <option value="mc" data-iconurl="img/MC.gif">Monaco Grand Prix - Circuit de Monaco</option>
        <option value="ca" data-iconurl="img/CA.gif">Canadian Grand Prix - Circuit Gilles Villeneuve</option>
        <option value="az" data-iconurl="img/AZ.gif">Azerbaijan Grand Prix - Baku City Circuit</option>
        <option value="at" data-iconurl="img/AT.gif">Austrian Grand Prix - Red Bull Ring</option>
        <option value="gb" data-iconurl="img/GB.gif">British Grand Prix - Silverstone Circuit</option>
        <option value="hu" data-iconurl="img/HU.gif">Hungarian Grand Prix - Hungaroring</option>
        <option value="be" data-iconurl="img/BE.gif">Belgian Grand Prix - Circuit de Spa Francorchamps</option>
        <option value="it" data-iconurl="img/IT.gif">Italian Grand Prix - Autodromo Nazionale di Monza</option>
        <option value="sg" data-iconurl="img/SG.gif">Singapore Grand Prix - Marina Bay Street Circuit</option>
        <option value="my" data-iconurl="img/MY.gif">Malaysian Grand Prix - Sepang International Circuit</option>
        <option value="jp" data-iconurl="img/JP.gif">Japanese Grand Prix - Suzuka Circuit</option>
        <option value="us" data-iconurl="img/US.gif">United States Grand Prix - Circuit of the Americas</option>
        <option value="mx" data-iconurl="img/MX.gif">Mexican Grand Prix - Autodromo Hermanos Rodriguez</option>
        <option value="br" data-iconurl="img/BR.gif">Brazilian Grand Prix - Autodromo de Interlagos</option>
        <option value="ae" data-iconurl="img/AE.gif">Abu Dhabi Grand Prix - Yas Marina Circuit</option>
    </select>
</div>

<script>
  $(document).ready(function() {
    $('#f1-circuits').customA11ySelect();
  });
</script>

Example 4

Programmers can allow additional options to be inserted into the dropdown, or even for the whole list of options to be rebuilt. In an event handler or callback function, you can call the .customA11ySelect() function again on the original <select> input you want to refresh with a parameter of 'refresh' (e.g.: $('#my-select').customA11ySelect('refresh');). In other words, programmers should add, remove, or change the options from the original (now-hidden) select dropdown, and then call .customA11ySelect('refresh') on that element to refresh the custom dropdown (don't try to modify the options directly on the custom dropdown). This functionality may be helpful if a user's selection from one select input determines what options are displayed for a second select input.


<label for="f1-country">F1 Driver Home Country</label> 
<select id="f1-country">
    <option value="all">All</option>
    <option value="AU">Australia</option>
    <option value="BE">Belgium</option>
    <option value="BR">Brazil</option>
    <option value="CA">Canada</option>
    <option value="DK">Denmark</option>
    <option value="FI">Finland</option>
    <option value="FR">France</option>
    <option value="DE">Germany</option>
    <option value="GB">Great Britain</option>
    <option value="MX">Mexico</option>
    <option value="NL">Netherlands</option>
    <option value="RU">Russia</option>
    <option value="ES">Spain</option>
    <option value="SE">Sweden</option>
</select>
<label for="f1-country-drivers">Drivers From That Country</label>
<select id="f1-country-drivers">
    <option value="rai" selected="selected">Kimi Räikkönen</option>
    <option value="vet">Sebastian Vettel</option>
    <option value="ham">Lewis Hamilton</option>
    <option value="bot">Valtteri Bottas</option>
    <option value="ric">Daniel Ricciardo</option>
    <option value="ver">Max Verstappen</option>
    <option value="per">Sergio Perez</option>
    <option value="oco">Esteban Ocon</option>
    <option value="gro">Romain Grosjean</option>
    <option value="mag">Kevin Magnussen</option>
    <option value="van">Stoffel Vandoorne</option>
    <option value="alo">Fernando Alonso</option>
    <option value="hul">Nico Hülkenberg</option>
    <option value="pal">Jolyon Palmer</option>
    <option value="eri">Marcus Ericsson</option>
    <option value="weh">Pascal Wehrlein</option>
    <option value="kvy">Daniil Kvyat</option>
    <option value="sai">Carlos Sainz Jr.</option>
    <option value="mas">Felipe Massa</option>
    <option value="str">Lance Stroll</option>
</select>
<label for="f1-driver-option">Or, Manually Add An Option</label>
<button type="button" id="f1-driver-add" class="example-button">Add</button>
<div class="input-wrap">
    <input type="text" id="f1-driver-option" placeholder="Type any name">
</div>

<script>
  $(document).ready(function() {
    $('#f1-country').change(function(event) {
        var country = this.value,
            driverOptions = '',
            f1drivers = [
                // an array of objects 
                // each with a 'name', 'val', and 'country'
                // (see source code for this page)
            ];
        for(i=0; i<f1drivers.length; i++) {
            if (country === f1drivers[i].country || 
                country === 'all') {
                driverOptions += '<option value="'+f1drivers[i].val+'">'+f1drivers[i].name+'</option>';
            }
        }
        $('#f1-country-drivers').html(driverOptions).customA11ySelect('refresh');
    });

    $('#f1-driver-add').click(function(event){
        event.preventDefault();
        var driverName = $('#f1-driver-option').val();
        if (driverName !== undefined && driverName !== '') {
            var driverVal = driverName.replace(' ','-'),
                theOption = '<option value="'+driverVal+'" selected="selected">'+driverName+'</option>';
            $('#f1-country-drivers').append(theOption).customA11ySelect('refresh');
        }
    });
  });
</script>