Динамические списки (Динамический select)
Создание нескольких html-списков, состав внутренних элементов некоторых из которых зависит от выбранного элемента в других списках
Как обычно проходит свой путь развития веб-разработчик-самоучка? Он занимается тем, что ему кажется более простым. Считает, что ему вполне достаточно и этого для решения тех задач, которые стоят перед ним на начальном этапе. Но рано или поздно любой веб-разработчик приходит к пониманию того, что его знаний ему катастрофически не достаточно. Человек, мало-мальски знающий PHP (и, наверное,
Но приходит он… и тут капитулируют все… Он заставляет неистово гуглить, усердно писать на форумы, искать, копать, латать дыры в знаниях. Он коварен. Он — динамический список, динамический select.
Почему? Что к этому приводит? Ну, так случается, что ВНЕЗАПНО нужно сделать так, чтобы при выборе одного параметра в раскрывающемся списке,
Подробнее изучим задачу. Допустим, у нас есть три поля раскрывающихся списков: Тип транспорта, Вид транспорта, Категории транспорта. В конечном итоге нам необходимо, чтобы пользователь указал нужную ему категорию транспорта. Но мы хотим упростить жизнь своему пользователю, и не желаем, чтобы он утруждал себя поиском нужной категории среди совсем ненужных ему данных. Ведь категорий транспорта может быть великое множество, но подразделяются они на разные виды и типы.
Мы решили, что нам хватит трех списков. Делая выбор в первом, пользователь получает отфильтрованный второй список. В него попадут только те значения, которые относятся к выбранной категории в первом списке. Далее, делая выбор во втором списке, пользователь получает третий, сформированный по описанному выше принципу.
Следующей задачей является вот что — нет смысла позволять пользователю раскрывать второй и третий список до того, как не будет сделан выбор в первом. Ведь что он там сможет увидеть? Данные туда подгружаются динамически, основываясь на выбранных значениях. Значит до момента выбора второй и третий списки должны быть неактивными.
И ещё одно уточнение. Если пользователь захочет изменить выбранные данные, зависимые от него поля должны быть сброшены в исходное состояние. Это должно происходить для того, чтобы в списках всегда были только значения, которые имеют отношение к выбранным категориям и типам.
Итак, создаем форму с тремя списками.
<!-- Пишем в соответствии со стандартом HTML5 →
<!DOCTYPE html>
<html>
<head>
<!-- Пишем заголовок документа →
<title>Динамические списки</title>
</head>
<body>
<!-- Форма для динамических списков →
<form action=»» method= «post» id= «dynamic_selects»>
<!-- Создаем заголовок для списка выбора типов транспорта →
<!-- Поле формы помещаем в контейнер с классом row →
<div class= «row»>
<label for= «type»>Тип транспорта:</label>
<!-- Создаем поле со списком →
<select id= «type»>
<!-- В список сразу внесем значение по умолчанию, а также
несколько значений видов транспорта. Предположим, что они нам известны заранее, и хранятся, допустим, в базе данных →
<option value= «0»>Выберите из списка</option>
<option value= «1»>Наземный транспорт</option>
<option value= «2»>Водный транспорт</option>
<option value= «3»>Воздушный транспорт</option>
</select>
</div>
<!-- Для списков видов и категорий транспорта мы создадим только значения по умолчанию,
остальные значения мы будем подгружать в них тогда, когда будет сделан выбор
в первом списке →
<div class= «row»>
<label for= «kind»>Вид транспорта:</label>
<!-- Так как это список зависит от выбора в первом списке, отключаем его, добавив
к тегу select атрибут disabled→
<select id= «kind» disabled>
<option value= «0»>Выберите из списка</option>
</select>
</div>
<div class= «row»>
<label for= «category»>Категория транспорта:</label>
<!-- Так как это список зависит от выбора во втором списке, отключаем его, добавив
к тегу select атрибут disabled→
<select id= «category» disabled>
<option value= «0»>Выберите из списка</option>
</select>
</div>
</form>
</body>
</html>
Создав данный HTML-документ с данным кодом и откроете его в своем броузере, вы увидите форму с тремя списками, два из которых будут неактивными. Не очень красиво они сейчас выглядят. Давайте изменим их внешний вид. Для этого нам нужно будет создать небольшой CSS-документ, который мы подключим в заголовке созданного ранее HTML-документа. В этот CSS-документ мы запишем следующее:
/**
* Особо внешний вид страницы «причесывать» не будем
* Просто оформим поля нашей формы
*/
/* Для контейнера, который содержит поля формы */
div.row {
margin-bottom:15px; /* Нижний отступ */
}
/* Заголовки полей форм будут на отдельной строке */
label {
display:block;
font-weight:bold; /* Сделаем их жирными */
margin-bottom:5px; /* Нижний отступ от заголовка */
}
/* Наши раскрывающиеся списки тоже «причешем» */
select {
width:300px; /* Ширина */
padding:3px 5px; /* Величина внутренних отступов: сверху/снизу, справа/слева */
}
Конечно же внешний вид формы у вас может быть своим: ) Что дальше? Давайте подумаем. При выборе значения в первом списке мы должны получить перечень значений для второго. Даже не хочу сейчас описывать решение этого процесса одним только PHP. Нужно помнить одно: если речь идёт о последствиях действий пользователя, значит это JavaScript.
Но писать пригодный код на чистом JavaScript довольно не просто. Язык и сам по себе довольно не прост в понимании для тех людей, которые только-только стали осваивать PHP, а вдобавок ещё и каждый производитель броузера по-своему реализовал его в своем продукте. Для того, чтобы не заботиться о кроссбраузерности, мы воспользуемся библиотекой jQuery. Вопросы, связанные с синтаксисом данной библиотеки, здесь мы не обсуждаем.
Заголовок нашего HTML-документа мы перепишем, добавив в него подключение файла CSS и подключение библиотеки jQuery, которую мы возьмём у Яндекса. Следом мы подключим JavaScript-файл, в котором будем писать собственный JavaScript-сценарий.
<!-- Пишем заголовок документа →
<title>Динамические списки</title>
<!-- Подключаем CSS-файл к документу →
<link rel= «stylesheet» type= «text/css» href= «style.css» />
<!-- Подключаем библеотеку jQuery, которая хранится на сервере Яндекса →
<script src= «http://yandex.st/jquery/1.10.2/jquery.min.js»></script>
<!-- Подключаем файл со своим собственным скриптом →
<script src= «my.script.js»></script>
Что должно происходить? При возникновении события выбора значения в первом списке, выбранное значение должно быть отправлено обработчику, который в ответ отправит перечень значений для второго списка, непосредственно связанных с первым. Этот перечень мы будем получать от сервера, так как предполагаем, что нужная нам информация хранится в базе данных (MySQL, к примеру). Значит из JavaScript-сценария нам нужно будет выполнить AJAX-запрос к серверу. После того, как сервер пришлет ответ, мы помещаем его во второй список, и делаем его активным. Начнем писать наш JavaScript-сценарий.
/**
* @author Evgeni Lezhenkin evgeni@lezhenkin.ru http://lezhenkin.ru
*
* В данном сценарии мы будем обрабатывать события выбора значений
* в списках тега select. Второе и третье поле списка являются зависимыми.
* При выборе значения в первом поле select должен отправляться AJAX-запрос,
* который вернет перечень значений для второго списка. Далее эти значения будут
* помещены во второй select, а сам список станет активным.
* После выбора значения во втором списке запускается тот же алгоритм, что описан
* выше.
*
* Все AJAX-запросы к серверу будут выполняться через скрипт query.php методом POST
*
* Если пользователь меняет значение в первом списке, все поля второго
* и третьего удаляются, и второй и третий списки становятся неактивными. Если * Меняется значение второго списка, то удаляются все значения для третьего,
* и он становится неактивным.
*
* Данный сценарий будет работать во всех броузерах благодаря библеотеке jQuery
*
* Здесь не будет описываться синтаксис самого jQuery, синтаксис JavaScript. Также
* здесь не будет подробно описываться инструментарий
*/
// Используем немедленно вызываемую функцию
(function () {
// Будем работать в соответствии с требованиями современного стандарта
// ECMAScript. Включим строгий режим.
«use strict»;
// Весь наш основной сценарий будет работать уже после загрузки документа
jQuery (function () {
// Пишем обработчик события для выбора значения в первом списке
// Нас интересует событие изменения значения поля
$ ('#type'). change (function () {
// При изменении значения первого списка мы должны удалить
// все имеющиеся значения во втором и третьем, а также
// сделать их неактивными
$ ('#kind, #category'). find ('option:not (:first)') // Ищем все теги option, не являющиеся тегом по умолчанию
.remove () // Удаляем эти теги
// Чтобы сделать поля неактивными, неправильно менять значение атрибута disabled
// Теперь нам нужно изменять значение свойства disabled объектов полей списка,
// так как мы работаем с ними через библиотеку jQuery
.end () // Возвращаемся к исходному объекту
.prop ('disabled',true) ; // Делаем поля неактивными
// Сохраним выбранное значение списка в переменную
var type_id = $ (this). val ();
// Если выбрано значение по умолчанию, ничего не делаем
if (type_id == 0) { return; }
// В ином случае нам необходимо отправить запрос на сервер
// AJAX-запрос к серверу мы выполним, используя метод jQuery ajax ()
$.ajax ({
type: «POST», // Тип запроса
url: «query.php», // Путь к сценарию, обработающему запрос
dataType: «json», // Тип данных, в которых сервер должен прислать ответ
data: «query=getKinds&type_id=» + type_id, // Строка POST-запроса
error: function () { // Обработчик, который будет запущен в случае неудачного запроса
alert ( «При выполнении запроса произошла ошибка: (») ; // Сообщение о неудачном запросе
},
success: function (data) { // Обработчик, который будет запущен после успешного запроса
// В ответ на наш запрос сервер должен прислать массив значений
// Мы его вставим в поле второго списка с помощью цикла for
for (var i = 0; i < data.length; i++) {
// Каждое полученное значение вставим в список видов транспорта
$ ('#kind'). append ('<option value=»' + data[i].kind_id + '">' + data[i].kind + '</option>') ;
}
// После того, как мы сформировали список, мы можем сделать его активным
// обратившись к его свойству disabled
$ ('#kind'). prop ('disabled', false) ; // Включаем поле
}
});
});
// Пишем обработчик события для выбора значения во втором списке
// Нас интересует событие изменения значения поля
$ ('#kind'). change (function () {
// При изменении значения второго списка мы должны удалить
// все имеющиеся значения в третьем, а также
// сделать его неактивными
$ ('#category'). find ('option:not (:first)') // Ищем все теги option, не являющиеся тегом по умолчанию
.remove () // Удаляем эти теги
// Чтобы сделать поле неактивным, неправильно менять значение атрибута disabled
// Теперь нам нужно изменять значение свойства disabled объекта поля списка,
// так как мы работаем с ним через библиотеку jQuery
.end () // Возвращаемся к исходному объекту
.prop ('disabled',true) ; // Делаем поле неактивным
// Сохраним выбранное значение списка в переменную
var kind_id = $ (this). val ();
// Сохраним выбранное значение типа транспорта в переменную
var type_id = $ ('#type'). val ();
// Если выбрано значение по умолчанию, ничего не делаем
if (type_id === 0) { return; }
// В ином случае нам необходимо отправить запрос на сервер
// AJAX-запрос к серверу мы выполним, используя метод jQuery ajax ()
$.ajax ({
type: "POST", // Тип запроса
url: "query.php", // Путь к сценарию, обработающему запрос
dataType: "json", // Тип данных, в которых сервер должен прислать ответ
data: "query=getCategories&type_id=" + type_id + "&kind_id=" + kind_id, // Строка POST-запроса
error: function () { // Обработчик, который будет запущен в случае неудачного запроса
alert ("При выполнении запроса произошла ошибка: (") ; // Сообщение о неудачном запросе
},
success: function (data) { // Обработчик, который будет запущен после успешного запроса
// В ответ на наш запрос сервер должен прислать массив значений
// Мы его вставим в поле третьего списка с помощью цикла for
for (var i = 0; i < data.length; i++) {
// Каждое полученное значение вставим в список категорий транспорта
$ ('#category'). append ('<option value="' + data[i].category_id + '">' + data[i].category + '</option>') ;
}
// После того, как мы сформировали список, мы можем сделать его активным
// обратившись к его свойству disabled
$ ('#category'). prop ('disabled', false) ; // Включаем поле
}
});
});
// Никакие обработчичик для поля третьего списка не нужны
}); // Функция ожидания загрузки документа jQuery
}) (); // Немедленно вызываемая функция
/**
* Вы должны понимать, что данный код далек от идеала и является лишь
* примером решения конкретной задачи — динамического пополнения полей
* формы select. Здесь опущено очень много моментов, связанных с отладкой,
* безопасностью и многим другим. И вы должны понимать это очень хорошо.
* Данный пример просто показывает инструмент, с помощью которого решается
* озвученная задача.
*/
В представленном коде для вас будет достаточно много нового. Я снабдил его обширными комментариями, которые помогут вам понять суть процесса. Теперь пришла очередь взглянуть на сценарий-обработчик AJAX-запросов. Он у нас находится в файлеquery.php.
<?php
/**
* @author Evgeni Lezhenkin evgeni@lezhenkin.ru http://lezhenkin.ru
*
* Скрипт, обрабатывающий POST-запросы от JavaScript-сценариев
* Скрипт получает строку запроса, а в ответ отправляет массив с данными
*/
// Непосредственно для этого скрипта мы создадим здесь массивы, в которых будут храниться
// значения, запрашиваемые JavaScript-сценарием. В ваших сценариях этих массивов, скорее всего,
// не будет. Информация, подобная этой, будет в вашей базе данных, и вам её придется оттуда
// извлечь. Как вы это сделаете, это уже ваши предпочтения
$types = array (
1 ≥ array (
// Наземный транспорт
1 ≥ 'Железнодорожный транспорт',
2 ≥ 'Автомобильный транспорт',
3 ≥ 'Ручной транспорт'
),
2 ≥ array (
// Водный транспорт
1 ≥ 'Речной транспорт',
2 ≥ 'Морской транспорт',
3 ≥ 'Подводный транспорт'
),
3 ≥ array (
// Воздушный транспорт
1 ≥ 'Самолеты',
2 ≥ 'Вертолеты',
3 ≥ 'Ракета (шаттл)'
)
);
$kinds = array (
// Наземный транспорт
1 ≥ array (
// Железнодорожный транспорт
1 ≥ array (
1 ≥ 'Электропоезд',
2 ≥ 'Дизельный поезд',
3 ≥ 'Дрезина'
),
// Автомобильный транспорт
2 ≥ array (
1 ≥ 'Легковой автомобиль',
2 ≥ 'Грузовой автомобиль',
3 ≥ 'Автобус'
),
// Ручной транспорт
3 ≥ array (
1 ≥ 'Тачка',
2 ≥ 'Тележка',
3 ≥ 'Велосипед'
)
),
// Водный транспорт
2 ≥ array (
// Речной транспорт
1 ≥ array (
1 ≥ 'Трамвай',
2 ≥ 'Теплоход',
3 ≥ 'Ракета'
),
// Морской транспорт
2 ≥ array (
1 ≥ 'Крейсер',
2 ≥ 'Круизный лайнер',
3 ≥ 'Баржа'
),
// Подводный транспорт
3 ≥ array (
1 ≥ 'Подводная лодка',
2 ≥ 'Батискаф',
3 ≥ 'Капсула смерти'
)
),
// Воздушный транспорт
3 ≥ array (
// Самолет
1 ≥ array (
1 ≥ 'Боинг',
2 ≥ 'Аэробус',
3 ≥ 'Руслан'
),
// Вертолеты
2 ≥ array (
1 ≥ 'МИ',
2 ≥ 'Апач',
3 ≥ 'Черная акула'
),
// Ракета (шаттл)
3 ≥ array (
1 ≥ 'Союз',
2 ≥ 'Апполон',
3 ≥ 'Дискавери',
4 ≥ 'Буран'
)
)
);
// Проверяем наличие переменной, которая укажет данному сценарию какие именно данные нужны
if (!isset ($_POST['query']) ||! $_POST['query']) {
exit ("Нет данных определяющих тип запроса");
}
else {
// Сохраняем строку запроса данных в отдельной переменной
$query = trim ($_POST['query']); // Очищаем от лишних пробелов
// Определяем тип запроса
switch ($query) {
case 'getKinds': // Запрос на получение видов транспорта
// Сохраним в переменную значение выбранного типа транспорта
$type_id = trim ($_POST['type_id']); // Очистим его от лишних пробелов
// Формируем массив с ответом
$result = NULL;
$i = 0;
foreach ($types[$type_id] as $kind_id ≥ $kind) {
$result[$i]['kind_id'] = $kind_id;
$result[$i]['kind'] = $kind;
$i++;
}
break;
case 'getCategories': // Запрос на получение категорий транспорта
// Сохраним в переменные значения выбранных типа транспорта и вида транспорта
$type_id = trim ($_POST['type_id']); // Очистим их от лишних пробелов
$kind_id = trim ($_POST['kind_id']);
// Формируем массив с ответом
$result = NULL;
$i = 0;
foreach ($kinds[$type_id][$kind_id] as $category_id ≥ $category) {
$result[$i]['category_id'] = $category_id;
$result[$i]['category'] = $category;
$i++;
}
break;
default:
// Если данные не определены
$result = NULL;
break;
}
}
// Преобразуем данные в формат json, чтобы их смог обработать JavaScript-сценарий, приславший запрос
echo json_encode ($result);
/**
* Данный код не идеален. Сама идея представления исходных данных о транспорте в виде массива очень
* далека от идеала. И вы должны понимать почему. Данные должны храниться в реляционной базе данных,
* а представленный вариант написания сценария является лишь простейшим примером, который не стоит
* в таком виде применять на практике. Вы здесь должны лишь понять принципы работы языка и взаимодействия
* между языками программирования
*/
?>
Можете проверять select-ы. Они отлично работают. Добавить ещё один select вы вполне сможете сами. Я помню, как много времени потратил
- Я опубликовал эту статью:17.12.2013
- 77 303