In this guide, we shall enhance the capabilities of the native select element with selectable options, search functionality, customizable items, and last but not least, a much better UI design. We'll mimic the native HTML select element and utilize JS classes to provide a user-friendly dropdown box with checkboxes.

The multi-select dropdown JS class will enable users to select multiple options within an elegant dropdown list with additional functions such as searching, selecting all options, and limiting the maximum number of items the user can select. To do that, we can leverage JavaScript, HTML, and CSS3.

One of the advantages of using a preexisting HTML form element is that the class will fall back to the original element if JavaScript is disabled in the user's browser, and therefore, the user can still interact with the form element. Not only that, but the entire class is written in pure JS, so we don't have to include additional libraries such as jQuery — why use jQuery when we can leverage ES6+.

So, without further ado, let's get started!

1. Stylesheet (CSS3)

The stylesheet will be responsible for our multi-select dropdown element's UI design and structure. We can leverage CSS3 to create a modernized appearance.

Create a new file called MultiSelect.css and add the following CSS code:

CSS
.multi-select {
  display: flex;
  box-sizing: border-box;
  flex-direction: column;
  position: relative;
  width: 100%;
  user-select: none;
}
.multi-select .multi-select-header {
  border: 1px solid #dee2e6;
  padding: 7px 30px 7px 12px;
  overflow: hidden;
  gap: 7px;
  min-height: 45px;
}
.multi-select .multi-select-header::after {
  content: "";
  display: block;
  position: absolute;
  top: 50%;
  right: 15px;
  transform: translateY(-50%);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23949ba3' viewBox='0 0 16 16'%3E%3Cpath d='M8 13.1l-8-8 2.1-2.2 5.9 5.9 5.9-5.9 2.1 2.2z'/%3E%3C/svg%3E");
  height: 12px;
  width: 12px;
}
.multi-select .multi-select-header.multi-select-header-active {
  border-color: #c1c9d0;
}
.multi-select .multi-select-header.multi-select-header-active::after {
  transform: translateY(-50%) rotate(180deg);
}
.multi-select .multi-select-header.multi-select-header-active + .multi-select-options {
  display: flex;
}
.multi-select .multi-select-header .multi-select-header-placeholder {
  color: #65727e;
}
.multi-select .multi-select-header .multi-select-header-option {
  display: inline-flex;
  align-items: center;
  background-color: #f3f4f7;
  font-size: 14px;
  padding: 3px 8px;
  border-radius: 5px;
}
.multi-select .multi-select-header .multi-select-header-max {
  font-size: 14px;
  color: #65727e;
}
.multi-select .multi-select-options {
  display: none;
  box-sizing: border-box;
  flex-flow: wrap;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 999;
  margin-top: 5px;
  padding: 5px;
  background-color: #fff;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  max-height: 200px;
  overflow-y: auto;
  overflow-x: hidden;
}
.multi-select .multi-select-options::-webkit-scrollbar {
  width: 5px;
}
.multi-select .multi-select-options::-webkit-scrollbar-track {
  background: #f0f1f3;
}
.multi-select .multi-select-options::-webkit-scrollbar-thumb {
  background: #cdcfd1;
}
.multi-select .multi-select-options::-webkit-scrollbar-thumb:hover {
  background: #b2b6b9;
}
.multi-select .multi-select-options .multi-select-option, .multi-select .multi-select-options .multi-select-all {
  padding: 4px 12px;
  height: 42px;
}
.multi-select .multi-select-options .multi-select-option .multi-select-option-radio, .multi-select .multi-select-options .multi-select-all .multi-select-option-radio {
  margin-right: 14px;
  height: 16px;
  width: 16px;
  border: 1px solid #ced4da;
  border-radius: 4px;
}
.multi-select .multi-select-options .multi-select-option .multi-select-option-text, .multi-select .multi-select-options .multi-select-all .multi-select-option-text {
  box-sizing: border-box;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: inherit;
  font-size: 16px;
  line-height: 20px;
}
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio {
  border-color: #40c979;
  background-color: #40c979;
}
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-radio::after, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-radio::after {
  content: "";
  display: block;
  width: 3px;
  height: 7px;
  margin: 0.12em 0 0 0.27em;
  border: solid #fff;
  border-width: 0 0.15em 0.15em 0;
  transform: rotate(45deg);
}
.multi-select .multi-select-options .multi-select-option.multi-select-selected .multi-select-option-text, .multi-select .multi-select-options .multi-select-all.multi-select-selected .multi-select-option-text {
  color: #40c979;
}
.multi-select .multi-select-options .multi-select-option:hover, .multi-select .multi-select-options .multi-select-option:active, .multi-select .multi-select-options .multi-select-all:hover, .multi-select .multi-select-options .multi-select-all:active {
  background-color: #f3f4f7;
}
.multi-select .multi-select-options .multi-select-all {
  border-bottom: 1px solid #f1f3f5;
  border-radius: 0;
}
.multi-select .multi-select-options .multi-select-search {
  padding: 7px 10px;
  border: 1px solid #dee2e6;
  border-radius: 5px;
  margin: 10px 10px 5px 10px;
  width: 100%;
  outline: none;
  font-size: 16px;
}
.multi-select .multi-select-options .multi-select-search::placeholder {
  color: #b2b5b9;
}
.multi-select .multi-select-header, .multi-select .multi-select-option, .multi-select .multi-select-all {
  display: flex;
  flex-wrap: wrap;
  box-sizing: border-box;
  align-items: center;
  border-radius: 5px;
  cursor: pointer;
  display: flex;
  align-items: center;
  width: 100%;
  font-size: 16px;
  color: #212529;
}

That's essentially all we need to present our multi-select dropdown element. You're welcome to customize the properties, such as the border color, font size, etc, to reflect your site's design.

We can include the stylesheet in our <head> section with the following tag:

HTML
<link href="MultiSelect.css" rel="stylesheet" type="text/css">

But we'll get more on that later in the examples section.

2. JavaScript Class

The JavaScript class includes a bunch of methods that replace the native select element with a much more customizable one. In addition to that, we can bind event handlers, configure options, and specify data while creating a new instance of the class.

Create a new file called MultiSelect.js and add the following code:

JS
/*
 * Created by David Adams
 * https://codeshack.io/multi-select-dropdown-html-javascript/
 * 
 * Released under the MIT license
 */
class MultiSelect {

    constructor(element, options = {}) {
        let defaults = {
            placeholder: 'Select item(s)',
            max: null,
            search: true,
            selectAll: true,
            listAll: true,
            closeListOnItemSelect: false,
            name: '',
            width: '',
            height: '',
            dropdownWidth: '',
            dropdownHeight: '',
            data: [],
            onChange: function() {},
            onSelect: function() {},
            onUnselect: function() {}
        };
        this.options = Object.assign(defaults, options);
        this.selectElement = typeof element === 'string' ? document.querySelector(element) : element;
        for(const prop in this.selectElement.dataset) {
            if (this.options[prop] !== undefined) {
                this.options[prop] = this.selectElement.dataset[prop];
            }
        }
        this.name = this.selectElement.getAttribute('name') ? this.selectElement.getAttribute('name') : 'multi-select-' + Math.floor(Math.random() * 1000000);
        if (!this.options.data.length) {
            let options = this.selectElement.querySelectorAll('option');
            for (let i = 0; i < options.length; i++) {
                this.options.data.push({
                    value: options[i].value,
                    text: options[i].innerHTML,
                    selected: options[i].selected,
                    html: options[i].getAttribute('data-html')
                });
            }
        }
        this.element = this._template();
        this.selectElement.replaceWith(this.element);
        this._updateSelected();
        this._eventHandlers();
    }

    _template() {
        let optionsHTML = '';
        for (let i = 0; i < this.data.length; i++) {
            optionsHTML += `
                <div class="multi-select-option${this.selectedValues.includes(this.data[i].value) ? ' multi-select-selected' : ''}" data-value="${this.data[i].value}">
                    <span class="multi-select-option-radio"></span>
                    <span class="multi-select-option-text">${this.data[i].html ? this.data[i].html : this.data[i].text}</span>
                </div>
            `;
        }
        let selectAllHTML = '';
        if (this.options.selectAll === true || this.options.selectAll === 'true') {
            selectAllHTML = `<div class="multi-select-all">
                <span class="multi-select-option-radio"></span>
                <span class="multi-select-option-text">Select all</span>
            </div>`;
        }
        let template = `
            <div class="multi-select ${this.name}"${this.selectElement.id ? ' id="' + this.selectElement.id + '"' : ''} style="${this.width ? 'width:' + this.width + ';' : ''}${this.height ? 'height:' + this.height + ';' : ''}">
                ${this.selectedValues.map(value => `<input type="hidden" name="${this.name}[]" value="${value}">`).join('')}
                <div class="multi-select-header" style="${this.width ? 'width:' + this.width + ';' : ''}${this.height ? 'height:' + this.height + ';' : ''}">
                    <span class="multi-select-header-max">${this.options.max ? this.selectedValues.length + '/' + this.options.max : ''}</span>
                    <span class="multi-select-header-placeholder">${this.placeholder}</span>
                </div>
                <div class="multi-select-options" style="${this.options.dropdownWidth ? 'width:' + this.options.dropdownWidth + ';' : ''}${this.options.dropdownHeight ? 'height:' + this.options.dropdownHeight + ';' : ''}">
                    ${this.options.search === true || this.options.search === 'true' ? '<input type="text" class="multi-select-search" placeholder="Search...">' : ''}
                    ${selectAllHTML}
                    ${optionsHTML}
                </div>
            </div>
        `;
        let element = document.createElement('div');
        element.innerHTML = template;
        return element;
    }

    _eventHandlers() {
        let headerElement = this.element.querySelector('.multi-select-header');
        this.element.querySelectorAll('.multi-select-option').forEach(option => {
            option.onclick = () => {
                let selected = true;
                if (!option.classList.contains('multi-select-selected')) {
                    if (this.options.max && this.selectedValues.length >= this.options.max) {
                        return;
                    }
                    option.classList.add('multi-select-selected');
                    if (this.options.listAll === true || this.options.listAll === 'true') {
                        if (this.element.querySelector('.multi-select-header-option')) {
                            let opt = Array.from(this.element.querySelectorAll('.multi-select-header-option')).pop();
                            opt.insertAdjacentHTML('afterend', `<span class="multi-select-header-option" data-value="${option.dataset.value}">${option.querySelector('.multi-select-option-text').innerHTML}</span>`);
                        } else {
                            headerElement.insertAdjacentHTML('afterbegin', `<span class="multi-select-header-option" data-value="${option.dataset.value}">${option.querySelector('.multi-select-option-text').innerHTML}</span>`);
                        }
                    }
                    this.element.querySelector('.multi-select').insertAdjacentHTML('afterbegin', `<input type="hidden" name="${this.name}[]" value="${option.dataset.value}">`);
                    this.data.filter(data => data.value == option.dataset.value)[0].selected = true;
                } else {
                    option.classList.remove('multi-select-selected');
                    this.element.querySelectorAll('.multi-select-header-option').forEach(headerOption => headerOption.dataset.value == option.dataset.value ? headerOption.remove() : '');
                    this.element.querySelector(`input[value="${option.dataset.value}"]`).remove();
                    this.data.filter(data => data.value == option.dataset.value)[0].selected = false;
                    selected = false;
                }
                if (this.options.listAll === false || this.options.listAll === 'false') {
                    if (this.element.querySelector('.multi-select-header-option')) {
                        this.element.querySelector('.multi-select-header-option').remove();
                    }
                    headerElement.insertAdjacentHTML('afterbegin', `<span class="multi-select-header-option">${this.selectedValues.length} selected</span>`);
                }
                if (!this.element.querySelector('.multi-select-header-option')) {
                    headerElement.insertAdjacentHTML('afterbegin', `<span class="multi-select-header-placeholder">${this.placeholder}</span>`);
                } else if (this.element.querySelector('.multi-select-header-placeholder')) {
                    this.element.querySelector('.multi-select-header-placeholder').remove();
                }
                if (this.options.max) {
                    this.element.querySelector('.multi-select-header-max').innerHTML = this.selectedValues.length + '/' + this.options.max;
                }
                if (this.options.search === true || this.options.search === 'true') {
                    this.element.querySelector('.multi-select-search').value = '';
                }
                this.element.querySelectorAll('.multi-select-option').forEach(option => option.style.display = 'flex');
                if (this.options.closeListOnItemSelect === true || this.options.closeListOnItemSelect === 'true') {
                    headerElement.classList.remove('multi-select-header-active');
                }
                this.options.onChange(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
                if (selected) {
                    this.options.onSelect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
                } else {
                    this.options.onUnselect(option.dataset.value, option.querySelector('.multi-select-option-text').innerHTML, option);
                }
            };
        });
        headerElement.onclick = () => headerElement.classList.toggle('multi-select-header-active');  
        if (this.options.search === true || this.options.search === 'true') {
            let search = this.element.querySelector('.multi-select-search');
            search.oninput = () => {
                this.element.querySelectorAll('.multi-select-option').forEach(option => {
                    option.style.display = option.querySelector('.multi-select-option-text').innerHTML.toLowerCase().indexOf(search.value.toLowerCase()) > -1 ? 'flex' : 'none';
                });
            };
        }
        if (this.options.selectAll === true || this.options.selectAll === 'true') {
            let selectAllButton = this.element.querySelector('.multi-select-all');
            selectAllButton.onclick = () => {
                let allSelected = selectAllButton.classList.contains('multi-select-selected');
                this.element.querySelectorAll('.multi-select-option').forEach(option => {
                    let dataItem = this.data.find(data => data.value == option.dataset.value);
                    if (dataItem && ((allSelected && dataItem.selected) || (!allSelected && !dataItem.selected))) {
                        option.click();
                    }
                });
                selectAllButton.classList.toggle('multi-select-selected');
            };
        }
        if (this.selectElement.id && document.querySelector('label[for="' + this.selectElement.id + '"]')) {
            document.querySelector('label[for="' + this.selectElement.id + '"]').onclick = () => {
                headerElement.classList.toggle('multi-select-header-active');
            };
        }
        document.addEventListener('click', event => {
            if (!event.target.closest('.' + this.name) && !event.target.closest('label[for="' + this.selectElement.id + '"]')) {
                headerElement.classList.remove('multi-select-header-active');
            }
        });
    }

    _updateSelected() {
        if (this.options.listAll === true || this.options.listAll === 'true') {
            this.element.querySelectorAll('.multi-select-option').forEach(option => {
                if (option.classList.contains('multi-select-selected')) {
                    this.element.querySelector('.multi-select-header').insertAdjacentHTML('afterbegin', `<span class="multi-select-header-option" data-value="${option.dataset.value}">${option.querySelector('.multi-select-option-text').innerHTML}</span>`);
                }
            });
        } else {
            if (this.selectedValues.length > 0) {
                this.element.querySelector('.multi-select-header').insertAdjacentHTML('afterbegin', `<span class="multi-select-header-option">${this.selectedValues.length} selected</span>`);
            }
        }
        if (this.element.querySelector('.multi-select-header-option')) {
            this.element.querySelector('.multi-select-header-placeholder').remove();
        }
    }

    get selectedValues() {
        return this.data.filter(data => data.selected).map(data => data.value);
    }

    get selectedItems() {
        return this.data.filter(data => data.selected);
    }

    set data(value) {
        this.options.data = value;
    }

    get data() {
        return this.options.data;
    }

    set selectElement(value) {
        this.options.selectElement = value;
    }

    get selectElement() {
        return this.options.selectElement;
    }

    set element(value) {
        this.options.element = value;
    }

    get element() {
        return this.options.element;
    }

    set placeholder(value) {
        this.options.placeholder = value;
    }

    get placeholder() {
        return this.options.placeholder;
    }

    set name(value) {
        this.options.name = value;
    }

    get name() {
        return this.options.name;
    }

    set width(value) {
        this.options.width = value;
    }

    get width() {
        return this.options.width;
    }

    set height(value) {
        this.options.height = value;
    }

    get height() {
        return this.options.height;
    }

}
document.querySelectorAll('[data-multi-select]').forEach(select => new MultiSelect(select));

The code will automatically create a new instance of the class if the data-multi-select attribute is specified on one or more select elements, meaning all we essentially have to do is include the JS file in our HTML document with:

HTML
<script src="MultiSelect.js"></script>

Preferably at the bottom of the document before the body closing tag.

However, if we want to customize it further with custom HTML, etc, we can dynamically create a new instance and include additional options. It's pretty straightforward, so you shouldn't encounter any issues getting started!

3. Examples

In this section, we'll cover practical examples that will help guide you into integrating the multi-select dropdown element in your own projects.

3.1. Simple Dropdown

Let's start with a simple dropdown element with minimal configuration.

Add the following code to your HTML document:

HTML
<label for="fruits">Fruits</label>
<select id="fruits" name="fruits" data-placeholder="Select fruits" multiple data-multi-select>
    <option value="Apple">Apple</option>
    <option value="Banana">Banana</option>
    <option value="Blackberry">Blackberry</option>
    <option value="Blueberry">Blueberry</option>
    <option value="Cherry">Cherry</option>
    <option value="Cranberry">Cranberry</option>
    <option value="Grapes">Grapes</option>
    <option value="Kiwi">Kiwi</option>
    <option value="Mango">Mango</option>
    <option value="Orange">Orange</option>
    <option value="Peach">Peach</option>
    <option value="Pear">Pear</option>
    <option value="Pineapple">Pineapple</option>
    <option value="Raspberry">Raspberry</option>
    <option value="Strawberry">Strawberry</option>
    <option value="Watermelon">Watermelon</option>
</select>

So here, we are basically creating our multi-select dropdown element using the native select element. The data-multi-select attribute will determine the type of element. We actually don't need to declare any JS code because all the data is specified within the select element, and to apply our customizable options, we can declare data attributes (data-placeholder, data-max, data-search, etc.).

The snippet above will resemble the following:

Multi-select dropdown interface with HTML, CSS, and JS

If we want to preselect some of the items, we can simply add the selected attribute, like so:

HTML
<option value="Cranberry" selected>Cranberry</option>

As we can see, the class utilizes native methods, so it's much easier to convert existing elements without the hassle of rewriting the entire code.

3.2. Customizing Options with Data Attributes

To customize existing select elements, we can specify data attributes based on our requirements. Let's go ahead and start applying new options to our multi-select dropdown box.

Limiting the Number of Selectable Items

To limit the number of items we can select in the dropdown, we can implement the data-max attribute to our element, like so:

HTML
<label for="cars">Car Manufacturers</label>
<select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" multiple data-multi-select>
    <option value="Audi">Audi</option>
    <option value="BMW">BMW</option>
    <option value="Chevrolet">Chevrolet</option>
</select>

In the above example, the user can only select two items from the dropdown list. If we want to increase/decrease the maximum items, we can increment/decrement the data-max value.

Search Function

The search function will enable us to enter a query inside an input box and subsequently filter the items. By default, the search function is enabled, but we can disable it by specifying the data-search attribute, like so:

HTML
<label for="cars">Car Manufacturers</label>
<select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" data-search="false" multiple data-multi-select>
    <option value="Audi">Audi</option>
    <option value="BMW">BMW</option>
    <option value="Chevrolet">Chevrolet</option>
</select>

Ideally, you would want to disable it if your dropdown list contains only a few items.

Select All Function

The select all function will enable us to check and uncheck all items in the dropdown list with a single click of a button. Like previously, the select all function is enabled by default, but we can disable it with:

HTML
<label for="cars">Car Manufacturers</label>
<select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" data-search="false" data-select-all="false" multiple data-multi-select>
    <option value="Audi">Audi</option>
    <option value="BMW">BMW</option>
    <option value="Chevrolet">Chevrolet</option>
</select>

And just like that, the select all function is disabled.

List All Function

The list all function basically populates all the items in the selected items area. We can disable it with the following code:

HTML
<label for="cars">Car Manufacturers</label>
<select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" data-search="false" data-select-all="false" data-list-all="false" multiple data-multi-select>
    <option value="Audi">Audi</option>
    <option value="BMW">BMW</option>
    <option value="Chevrolet">Chevrolet</option>
</select>

If disabled, it will appear in "X selected" format as opposed to populating each individual item.

Useful if you expect the user to select a large number of items from the dropdown list. It will prevent the UI from being cumbersome.

Adjusting the Height and Width

We can set the default height and width in our stylesheet, but if we want to change the size for a particular dropdown element, we can implement the data-width and data-height attributes, like so:

HTML
<label for="cars">Car Manufacturers</label>
<select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" data-search="false" data-select-all="false" data-list-all="false" data-width="300px" data-height="50px" multiple data-multi-select>
    <option value="Audi">Audi</option>
    <option value="BMW">BMW</option>
    <option value="Chevrolet">Chevrolet</option>
</select>

Both options aren't mandatory, so you can remove one or the other. The size can be calculated in different formats (pixels, percentages, ems, etc.).

And to customize the size of the dropdown list, we can implement the data-dropdown-width and data-dropdown-height attributes.

With all the options applied above, the multi-select dropdown will resemble the following:

Minimal multi-select dropdown interface with HTML, CSS, and JS

Next up, we'll delve into more advanced methods.

3.3. Dynamic Creation with Custom HTML

Finally, we've reached the fun part of the article! In this section, we'll explore various methods to dynamically generate our multi-select dropdown boxes using JavaScript.

Let's go ahead and create our dynamic select container with the following code:

HTML
<label for="dynamic">Dynamic Select</label>
<select id="dynamic" name="dynamic"></select>

The type of the element is irrelevant, meaning we could use a DIV container as opposed to the select element. It's entirely up to you!

Right then! This is where it gets interesting... Let's create a new instance of the MultiSelect class and add some data with custom HTML content. To do that, add the following code to your HTML document:

HTML
<script>
// Initialize the Multi Select dropdown
new MultiSelect('#dynamic', {
    data: [
        {
            value: 'opt1',
            text: 'Option 1'
        },
        {
            value: 'opt2',
            html: '<strong>Option 2 with HTML!</strong>'
        },
        {
            value: 'opt3',
            text: 'Option 3',
            selected: true
        },
        {
            value: 'opt4',
            text: 'Option 4'
        },
        {
            value: 'opt5',
            text: 'Option 5'
        }
    ],
    placeholder: 'Select an option',
    search: true,
    selectAll: true,
    listAll: false,
    max: 2
});
</script>

The first parameter selects the element in our HTML document — there are various techniques to select elements. In our case, the hashtag character is prepended to the value, so it will select the element with the ID attribute followed by the value.

The data array consists of objects that will populate items in our dropdown. If we want to customize individual items, we can add the html property (see the second item in the array), which will override the text property.

TipYou can declare multiple instances of the MultiSelect class with different parameters, meaning you can have more than one multi-select dropdown.

With all the data attributes we implemented earlier, we can apply them in a similar format as shown in the example.

Change Event Listener

When the dropdown list has been changed, the change method will be triggered. We can add the event listener with the following code:

HTML
<script>
// Initialize the Multi Select dropdown
new MultiSelect('#dynamic', {
    data: ...,
    onChange: function(value, text, element) {
        console.log('Change:', value, text, element);
    }
});
</script>

That will output the arguments to the browser console (item value, etc.).

Select Event Listener

In some cases, we want to execute an event listener that will be triggered when an item is selected. To do that, we can add the following:

HTML
<script>
// Initialize the Multi Select dropdown
new MultiSelect('#dynamic', {
    data: ...,
    onSelect: function(value, text, element) {
        console.log('Selected:', value, text, element);
    }
});
</script>

And now, when an item is selected, the item args will be logged to the browser console.

Unselect Event Listener

But what about unselect? We've got you covered! Simply add the following:

HTML
<script>
// Initialize the Multi Select dropdown
new MultiSelect('#dynamic', {
    data: ...,
    onUnselect: function(value, text, element) {
        console.log('Unselected:', value, text, element);
    }
});
</script>

Pretty neat, right? With the event listeners listed above, there are no limits to what you can do with the multi-select class.

3.4. Full Source Code

Below is the full source code with all the examples outlined in this article.

HTML
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
        <meta name="viewport" content="width=device-width,minimum-scale=1">
		<title>Multi Select JS</title>
        <!-- Include the Multi Select stylesheet -->
        <link href="MultiSelect.css" rel="stylesheet" type="text/css">
        <style>
        * {
            box-sizing: border-box;
            font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
            font-size: 16px;
        }
        body {
            margin: 0;
            padding: 15px;
            background-color: #f3f4f7;
        }
        form {
            display: flex;
            flex-direction: column;
            margin: 100px auto;
            padding: 40px 40px 60px;
            max-width: 500px;
            width: 100%;
            background-color: #fff;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        form h1 {
            margin: 10px 0 5px;
            font-size: 24px;
            font-weight: 500;
            color: #474b50;
        }
        form label {
            margin: 25px 0 10px;
            font-weight: 500;
            color: #474b50;
        }
        </style>
	</head>
	<body>
        <form>

            <h1>Multi Select Dropdown</h1>

            <label for="fruits">Fruits</label>
            <select id="fruits" name="fruits" data-placeholder="Select fruits" multiple data-multi-select>
                <option value="Apple">Apple</option>
                <option value="Banana">Banana</option>
                <option value="Blackberry">Blackberry</option>
                <option value="Blueberry">Blueberry</option>
                <option value="Cherry">Cherry</option>
                <option value="Cranberry">Cranberry</option>
                <option value="Grapes">Grapes</option>
                <option value="Kiwi">Kiwi</option>
                <option value="Mango">Mango</option>
                <option value="Orange">Orange</option>
                <option value="Peach">Peach</option>
                <option value="Pear">Pear</option>
                <option value="Pineapple">Pineapple</option>
                <option value="Raspberry">Raspberry</option>
                <option value="Strawberry">Strawberry</option>
                <option value="Watermelon">Watermelon</option>
            </select>

            <label for="cars">Car Manufacturers</label>
            <select id="cars" name="cars" data-placeholder="Select car manufacturers" data-max="2" data-search="false" data-select-all="false" data-list-all="false" data-width="300px" data-height="50px" multiple data-multi-select>
                <option value="Audi">Audi</option>
                <option value="BMW">BMW</option>
                <option value="Chevrolet">Chevrolet</option>
            </select>

            <label for="dynamic">Dynamic Select</label>
            <select id="dynamic" name="dynamic"></select>

        </form>
        <!-- Include the Multi Select JS class -->
        <script src="MultiSelect.js"></script>
        <script>
        // Initialize the Multi Select dropdown
        new MultiSelect('#dynamic', {
            data: [
                {
                    value: 'opt1',
                    text: 'Option 1'
                },
                {
                    value: 'opt2',
                    html: '<strong>Option 2 with HTML!</strong>'
                },
                {
                    value: 'opt3',
                    text: 'Option 3',
                    selected: true
                },
                {
                    value: 'opt4',
                    text: 'Option 4'
                },
                {
                    value: 'opt5',
                    text: 'Option 5'
                }
            ],
            placeholder: 'Select an option',
            search: true,
            selectAll: true,
            listAll: false,
            max: 2,
            onChange: function(value, text, element) {
                console.log('Change:', value, text, element);
            },
            onSelect: function(value, text, element) {
                console.log('Selected:', value, text, element);
            },
            onUnselect: function(value, text, element) {
                console.log('Unselected:', value, text, element);
            }
        });
        </script>

    </body>
</html>

Don't forget to add the MultiSelect.css stylesheet and MultiSelect.js JavaScript class files to your project folder.

4. Reference

Below, you'll find the reference tables for the MultiSelect.js class.

Option Type Default Description
placeholder String Select item(s) The default placeholder text for the multi-select element.
name String empty The name attribute for the select element.
max Number null The maximum number of items that can be selected. If null, there is no limit.
search Boolean true Determines whether the search function is enabled or disabled.
selectAll Boolean true Determines whether the select all function is enabled or disabled.
listAll Boolean true Determines whether the list all function is enabled or disabled. If disabled, the selected value will appear as "X selected".
width String empty The width of the multi-select element, specified as a CSS value (e.g., '100px', '50%').
height String empty The height of the multi-select element, specified as a CSS value.
dropdownWidth String empty The width of the multi-select dropdown element, specified as a CSS value (e.g., '100px', '50%').
dropdownHeight String empty The height of the multi-select dropdown element, specified as a CSS value.
data Array empty An array of objects representing each item in the select element. See the reference table below.
onChange Function function() {} A callback function that's executed when the selected item changes. It receives the selected value and other relevant data as arguments.
onSelect Function function() {} A callback function that's executed when an item is selected.
onUnselect Function function() {} A callback function that's executed when an item is unselected.

The reference table for the data array.

Property Type Description
value String The value attribute of the item in the multi-select element.
text String The display text for the item.
selected Boolean Determines whether the option is selected by default.
html String Custom HTML content for the item. If provided, it overrides the text property.

To apply the properties to inline select elements, we can prepend data- to the property name and declare it in our element along with the value.

Conclusion

Modernizing the native select elements is imperative for sites seeking long-term growth and user-friendly design. With modern JS, the possibilities are endless. That being said, this guide aims to cover all aspects related to multi-select dropdowns with practical examples and a robust JS class.

Looking for another UI element with customizable options? Let us know your suggestions in the comments section below. We'd love to hear from you!

You can download the full source code from our GitHub repository: https://github.com/codeshackio/multi-select-dropdown-js

Thanks for reading!

Released under the MIT license.