Каскад раскрывающихся списков с использованием JQuery

При создании приложений для Web часто бывает необходимо установить в элементе select варианты выбора, значения которых зависят от параметров другого элемента select. Простой пример – выбор страны в одном раскрывающемся списке приводит к загрузке в следующий такой список перечня автомобилей, производимых в этой стране. А при выборе марки автомобиля, в следующий список загружаются модели этого авто. Вот и давайте попробуем вместе решить эту задачу, создав каскад раскрывающихся списков.
Для начала определим задачу: первый список будет содержать предварительно загруженные названия стран и всегда находиться в активном состоянии. Два оставшихся списка – выбора авто и выбора модели, пустые и находятся в неактивном состоянии. После того, как в первом списке будет выбрана страна необходимо заполнить второй список названиями автомобилей и перевести его в активное состояние. А после выбора названия автомобиля – заполнить список названиями моделей именно этого автомобиля и тоже активировать список. Кроме того, мы должны гарантировать, что пользователь никогда не сможет выбрать автомобиль «HONDA Vectra произведеный в Ю.Корее». Для обмена данными с сервером будем использовать популярный нынче формат JSON.
Итак, задача определена. Поехали?
Начнем с чего-нибудь очень простого. Например с подключения библиотеки jQuery и создания HTML-разметки:
В HEAD страницы подключаем библиотеку:
<script src="js/jquery-1.3.1.js" type="text/javascript"></script> |
В BODY создаем HTML-разметку:
<div> <label>Страна</label><br /> <select id="country"> <option value="">Выбрать страну</option> <option value="1">Германия</option> <option value="2">Ю.Корея</option> <option value="3">Япония</option> </select> </div> <div> <label>Автомобиль</label><br /> <select id="auto" disabled="disabled"></select> </div> <div> <label>Модель</label><br /> <select id="model" disabled="disabled"></select> </div> |
Легко и просто! Каждому списку – по id. Первый раскрывающийся список заполняем названиями стран. Два других ничем не заполняем и мало того, устанавливаем в неактивное состояние с помощью атрибута disabled. Описания стилей я приводить не буду – это дело вкуса каждого и не в тему этой статьи.
Что дальше? Задача оказалась не так уж и проста, как казалось в начале. Поэтому предлагаю пораскинуть мозгами и решить, какие общие действия мы будем производить над всеми списками. Я придумал два – заполнение списка и очистка списка. Давайте оформим эти операции в виде методов jQuery, чтобы иметь возможность в любой момент вызвать этот метод в цепочке команд jQuery.
Расширение для jQUery может быть либо методом, оперирующим с набором элементов, либо вспомогательной функцией, которая определяется непосредственно в $. Простой пример: в составе библиотеки есть метод each(callback), который работает как раз с набором элементов, выполняя функцию, определенную в параметре callback в контексте каждого элемента набора и вспомогательная функция $.each(object,callback), которая применяется для обхода массива или объекта, переданного в первом параметре с выполнением функции callback для каждого элемента массива(объекта). Почувствовали разницу?
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 | (function($){ // очищаем select $.fn.clearSelect = function(){ return this.each(function(){ if(this.tagName=='SELECT'){ this.options.length = 0; $(this).attr('disabled','disabled'); } }); } // заполняем select $.fn.fillSelect = function(dataArray){ return this.clearSelect().each(function(){ if(this.tagName=='SELECT') { var currentSelect = this; $.each(dataArray,function(index,data){ var option = new Option(data.text,data.value); if($.support.cssFloat) { currentSelect.add(option,null); }else{ currentSelect.add(option); } }); } }); } })(jQuery); |
Итак, что мы тут написали?
При написании метода clearSelect() мы воспользовались методом each(callback) для того, чтобы произвести необходимые операции над каждым элементом, переданным в наборе jQuery. Сначала производим проверку – если tagName переданного элемента действительно SELECT, то удаляем все элементы option и устанавливаем атрибуту disabled значение disabled.
Метод fillSelect(dataArray) несколько сложнее. Он получает в качестве параметра массив dataArray, содержащий данные, которыми необходимо заполнить выпадающий список. Правда перед тем, как заполнять список, мы вызовем метод clearSelect, чтобы очистить список от возможно оставшихся там данных. С помощью метода each(callback) мы перебираем все элементы, переданные в наборе jQuery, проверяем, действительно ли это элемент select и, если да, то сначала запоминаем его в переменной currentSelect. Затем воспользуемся вспомогательной функцией $.each(object,callback) для обхода массива dataArray. В каждой итерации создаем элемент options, заполняем его соответствующими данными и добавляем в select.
При добавлении option мы вынуждены учитывать различия между браузерами. К счастью и тут поможет jQuery. Здесь используем новую вспомогательную функцию $.support, которая появилась в jQuery с версии 1.3 взамен $.browser. Проверим значение свойства cssFloat – false укажет нам на IE, а true на FF, Opera, Safari. Вы можете применить свой способ проверки.
Обратите внимание: при написании метода fillSelect(dataArray) мы использовали метод each(callback) и вспомогательную функцию $.each(object,callback) – это разные вещи.
Теперь оба наших новых метода могут работать с набором элементов jQuery, который им будет передан. Этот код можно было бы вынести в отдельный файл – к тому же эти методы могут пригодиться и в других приложениях, а использовать их можно проще простого:
1 2 3 4 | // очищаем select $('select').clearSelect(); // заполняем select $('select').fillSelect(dataArray); |
Двигаемся дальше. Теперь мы напишем пару функций, которые будут обслуживать списки выбора автомобиля и выбора модели.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // выбор автомобиля function adjustAuto(){ var countryValue = $('#country').val(); var tmpSelect = $('#auto'); if(countryValue.length == 0){ tmpSelect.attr('disabled','disabled'); tmpSelect.clearSelect(); adjustModel(); } else{ $.getJSON( 'cascadeSelectAuto.php', {country:countryValue}, function(data) { tmpSelect.fillSelect(data).attr('disabled',''); adjustModel(); }); } }; |
Здесь мы сначала запоминаем в переменной countryValue значение, выбранное в списке стран #country. В переменной tmpSelect будем хранить ссылку на элемент select с идентификатором #auto – список выбора автомобиля. Далее следует проверка:
Если страна не выбрана, то мы устанавливаем атрибуту disabled элемента select с идентификатором #auto значение ‘disabled’ и вызываем метод clearSelect() для очистки этого списка от возможно находящихся там значений. Также мы вызовем функцию adjustModel() (про нее чуть позже).
Иначе, мы отправляем с помощью вспомогательной функции $.getJSON запрос к файлу cascadeSelectAuto.php, передавая в качестве данных значение, сохраненное в переменной countryValue. При получении ответа сервера вызываем функцию, в которой определена нужная нам последовательность действий. А именно – к переменной tmpSelect (не забыли – это список с идентификатором #auto) применяем нами же написанный метод fillSelect(data), который заполнит список данными, которые получены от сервера и наконец переводим элемент в активное состояние с помощью атрибута disabled. Остается не забыть вызвать функцию adjustModel(), чтобы обработать выпадающий список моделей.
Функция adjustModel(), которая обрабатывает список моделей, очень похожа на adjustAuto(), поэтому нет смысла ее детально рассматривать. Обратите внимание разве на то, что теперь мы запоминаем значение из списка не только выбранной страны, но и выбранного автомобиля. И еще в if – else не вызываем что-то похожее на adjustModel(), поскольку список выбора модели у нас последний – дальше обрабатывать просто нечего.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // выбор модели function adjustModel(){ var countryValue = $('#country').val(); var autoValue = $('#auto').val(); var tmpSelect = $('#model'); if(countryValue.length == 0||autoValue.length == 0) { tmpSelect.attr('disabled','disabled'); tmpSelect.clearSelect(); } else{ $.getJSON( 'cascadeSelectModel.php', {country:countryValue,auto:autoValue}, function(data) { tmpSelect.fillSelect(data).attr('disabled',''); }); } }; |
Поздравляю! Самая сложная часть пути уже позади! Остались сущие пустяки.
1 2 3 | $('#country').change(function(){ adjustAuto(); }).change(); |
Связываем с выпадающим списком стран событие change(fn), при наступлении которого вызываем adjustAuto() – функцию, которая заполнит выпадающий список автомобилей. В цепочке команд вызываем change(), чтобы вызвать событие при начальной загрузке страницы.
$('#auto').change(adjustModel); |
С выпадающим списком автомобилей связываем событие change(fn), при наступлении которого будет выполнена функция adjustModel(), которая заполнит список моделей.
1 2 3 4 5 | $('#model').change(function(){ if($(this).val().length != 0) { alert('Выбор сделан!'); } }); |
И наконец, при изменении списка моделей выполним небольшую проверку – если действительно выбрана какая-либо модель, выведем в alert сообщение. Ну а в действительности тут можно придумать все, что угодно. Например вывести подробную информацию о технических характеристиках, красивую фотографию авто, чтобы его захотелось купить и конечно цену…
Вы можете скачать архив, который содержит файлы примера (в том числе cascadeSelectAuto.php и cascadeSelectModel.php):
Здравствуйте! Большое спасибо за статью! Сейчас создаю сайт, и очень необходим зависимый список страны-области-города. Скажите пожалуйста, какой код нужно вставить на сайт, чтобы это работало? Заранее спасибо за помощь!