Journal.
Random thoughts and code snippets.

jQuery Multi-select

jQuery Multi-select

  • by Daniel Stainback
  • posted May 05, 2016

Quick and simple way to support multi-select using Bootstrap's dropdown component.

Primary jQuery file (jquery.multiselect.js):

/**
 * @file jquery.multiselect.js
 * @brief Support for multi-select dropdowns.
 * @author Daniel Stainback (Torann)
 * @version 0.1
 * @site https://lyften.com/journal/jquery-multiselect.html
 * @license BSD 2-Clause
 */

!function ($) {
    "use strict";// jshint ;_;

    /**
     * Constructor to create a new multi-select using the given select.
     *
     * @param {jQuery} element
     * @param {Object} options
     * @returns {MultiSelect}
     */
    var MultiSelect = function (element, options) {
        this.$element = $(element);
        this.limit = this.$element.data('limit');

        this.options = $.extend(true, {
            name: this.$element.prop('name'),
            disabled: this.$element.is(':disabled')
        }, MultiSelect.DEFAULTS, options);

        // Ensure the option name is an array
        if (this.options.name.slice(-2) !== '[]') {
            this.options.name = this.options.name + '[]';
        }

        // Limit attribute
        var data_limit = this.limit ? ' data-limit="' + this.limit + '"' : '';

        // Create elements
        this.$plaeholder = $('<a href="#" data-toggle="dropdown" aria-expanded="true"/>').insertAfter(this.$element);
        this.$dropdown = $('<ul class="dropdown-menu" role="menu"' + data_limit + '/>').insertAfter(this.$plaeholder);

        // Text
        var txt = this.$element.find('option[value=""]').text() || 'Select one';
        this.$plaeholder.data('plaeholder', txt).html(txt);

        // Build select all if enabled.
        this.build();

        this.$element.remove();
    };

    MultiSelect.DEFAULTS = {
        disabled: false
    };

    MultiSelect.prototype.build = function () {
        this.$element.find('option').each($.proxy(function (index, element) {
            this.createOptionValue(index, element);
        }, this));

        this.updatePlaceHolder();
    };

    MultiSelect.prototype.createOptionValue = function (index, element) {
        var $element = $(element);

        if ($element.val() === '') return;

        // Support the label attribute on options.
        var label = $.trim($element.text());
        var input = '<input type="checkbox" id="option-checkbox-' + index + '" name="' + this.options.name + '" data-label="'+ label +'"><label for="option-checkbox-'+index+'">' + label + '</label>';

        var $option = $('<li role="presentation"><div class="form-group">' + input + '</div></li>');
        var $checkbox = $option.find('[type="checkbox"]');

        // Set value
        $checkbox.val($element.val());

        // Add option to dropdown
        this.$dropdown.append($option);

        if (this.options.disabled === true || $element.is(':disabled')) {
            $checkbox.attr('disabled', 'disabled');
        }

        $checkbox.prop('checked', $element.prop('selected') || false).on('change', $.proxy(function (ev) {
            this.updatePlaceHolder();
        }, this));
    };

    MultiSelect.prototype.updatePlaceHolder = function () {
        var txt = '',
            $inputs = this.$dropdown.find('input:checked');

        $inputs.each(function () {
            txt += $(this).data('label') + ', ';
        });

        // Restrict the number of selected items
        if (this.limit) {
            this.$dropdown
                .find('input:not(:checked)')
                .toggleProp('disabled', ($inputs.length >= this.limit));
        }

        this.$plaeholder.text(txt ? $.trim(txt).replace(/(^,)|(,$)/g, '') : this.$plaeholder.data('plaeholder'));
    };

    $.fn.multiselect = function (option, parameter) {
        return this.each(function () {
            var data = $(this).data('multi-select');
            var options = typeof option === 'object' && option;

            // Initialize the multi-select.
            if (!data) {
                data = new MultiSelect(this, options);
                $(this).data('multi-select', data);
            }

            // Call multi-select method.
            if (typeof option === 'string') {
                data[option](parameter);

                if (option === 'destroy') {
                    $(this).data('multi-select', false);
                }
            }
        });
    };

    $.fn.multiselect.Constructor = MultiSelect;

    $(function () {
        $('select[multiple]').multiselect();
    });

}(window.jQuery);