Cascading Autocompletes using jQuery UI


Yesterday I got a new requirement for the application I am working on.
I had several jQuery UI autocompletes and I needed to add cascading functionality. This means that each autocomplete in the group affects the following autocomple’s possible values. If there is no value selected in the first autocomplete – the next autocomplete must be disabled. When the user selects a value on the first autocomplete – the next autocomplete becomes enabled.

After searching the web I realized that the best option for me is to write my own jQuery plugin that manages the autocompletes cascading.
The plugin gets as input a sequence of jQuery UI auocomplete elements and adds the cascading functionality. The idea under my plugin is to override each autocomplete’s change and select events, and enable or disable the next autocomplete in the sequence according to the selected value.

Here is the code:

Cascading Autocompletes plugin
(function($) {

    function activateChild() {
        var child = $(this);
        child
            .prop("disabled", false)
            .removeClass("disabled");
        if (child.autocomplete) {
            child.autocomplete("enable");
        }
    }

    function deactivateChild() {
        var child = $(this);
        child
            .prop("disabled", true)
            .val("")
            .addClass("disabled");
        if (child.autocomplete) {
            child.autocomplete("disable");
        }

        var subChild = child.data("cascade-child");
        if (subChild) {
            deactivateChild.call(subChild);
        }
    }

    function eventOverride(originalEvent) {
        var child = this;
        return function(event, ui) {
            var isChildActivate = child.prop("disabled") === false;
            if (!ui.item && isChildActivate) {
                deactivateChild.call(child);
            } else if (ui.item && !isChildActivate) {
                activateChild.call(child);
            }
            if (typeof originalEvent === "function") {
                originalEvent.call(child, event, ui);
            }
        };
    }

    $.fn.cascade = function(child) {
        var parent = this;
        child = $(child);

        if (parent.autocomplete) {
            var originalChange = parent.autocomplete("option", "change");
            var originalSelect = parent.autocomplete("option", "select");
            parent.data("cascade-child", child);
            parent.autocomplete("option", "change", eventOverride.call(child, originalChange));
            parent.autocomplete("option", "select", eventOverride.call(child, originalSelect));
        }

        deactivateChild.call(child);

        return parent;
    };

    $.cascadingAutocompletes = function(autocompletes) {
        for (var i = 0; i < autocompletes.length - 1; i++) {
            if (autocompletes[i]) {
                $(autocompletes[i]).cascade(autocompletes[i + 1]);
            }
        }
    };
})(jQuery);

Usage example:

Cascading Autocompletes plugin Usage
$.cascadingAutocompletes(["#country", "#city", "#street"]);

Demo & Download

Hope this helps!