Связанные динамичные списки Select без перезагрузки страницы


Очень часто сталкиваюсь с вопросами реализации связанных динамичных выпадающих списков (select), очень много методов, самых различных, даже JSON в некоторых используется, что на мой скромный взгляд только дублирует данные из базы и создаёт банальную избыточность.

Однажды познакомился с $.ajax (JQuery), который может передавать данные методами GET или POST и понял, что связанные списки, это проще простого.

Ниже описан метод с использованием базы данных. Вся красота решения состоит в написании двух маленьких (похожих друг на друга) функций на JavaScript и одного исполняемого файла на PHP с кодом в 20 строк алгоритма переключения и генерации.

Нужно отметить, что есть методы, в которых можно генерировать динамические списки неограниченное количесво раз и все они будут связаны. В данной статье приводятся всего три списка, что, по-моему, достаточно для понимания метода как такового и создания на его основе улучшенных алгоритмов реализации задачи.

Постановка задачи: связать три (страна, область, город) выпадающих списка используя $.ajax (JQuery) без перезагрузки страницы, причём данные берутся из базы mySQL.


Реализация

Для начала создадим связанные таблицы в базе:

CREATE TABLE `tbl_country` (
  `id_country` int(11) NOT NULL AUTO_INCREMENT,
  `country` varchar(32) NOT NULL,
  PRIMARY KEY (`id_country`),
  UNIQUE KEY `country` (`country`)
);

INSERT INTO `tbl_country` VALUES(1, 'Украина');
INSERT INTO `tbl_country` VALUES(2, 'Россия');
INSERT INTO `tbl_country` VALUES(3, 'Белоруссия');
INSERT INTO `tbl_country` VALUES(4, 'Израиль');

CREATE TABLE `tbl_region` (
  `id_region` int(11) NOT NULL AUTO_INCREMENT,
  `id_country` int(11) NOT NULL,
  `region` varchar(32) NOT NULL,
  PRIMARY KEY (`id_region`),
  UNIQUE KEY `region` (`region`),
  KEY `id_country` (`id_country`)
);

INSERT INTO `tbl_region` VALUES(1, 1, 'АР Крым');
INSERT INTO `tbl_region` VALUES(2, 1, 'Винницкая обл.');
INSERT INTO `tbl_region` VALUES(3, 1, 'Днепропетровская обл.');
INSERT INTO `tbl_region` VALUES(4, 1, 'Донецкая обл.');
INSERT INTO `tbl_region` VALUES(5, 1, 'Житомирская обл.');
INSERT INTO `tbl_region` VALUES(6, 1, 'Закарпатская обл.');
INSERT INTO `tbl_region` VALUES(7, 1, 'Запорожская обл.');
INSERT INTO `tbl_region` VALUES(8, 1, 'Ивано-Франковская обл.');
INSERT INTO `tbl_region` VALUES(9, 1, 'Киевская обл.');
INSERT INTO `tbl_region` VALUES(10, 1, 'Кировоградская обл.');
INSERT INTO `tbl_region` VALUES(11, 1, 'Луганская обл.');
INSERT INTO `tbl_region` VALUES(12, 1, 'Волынская обл.');
INSERT INTO `tbl_region` VALUES(13, 1, 'Львовская обл.');
INSERT INTO `tbl_region` VALUES(14, 1, 'Николаевская обл.');
INSERT INTO `tbl_region` VALUES(15, 1, 'Одесская обл.');
INSERT INTO `tbl_region` VALUES(16, 1, 'Полтавская обл.');
INSERT INTO `tbl_region` VALUES(17, 1, 'Ровенская обл.');
INSERT INTO `tbl_region` VALUES(18, 1, 'Сумская обл.');
INSERT INTO `tbl_region` VALUES(19, 1, 'Тернопольская обл.');
INSERT INTO `tbl_region` VALUES(20, 1, 'Харьковская обл.');
INSERT INTO `tbl_region` VALUES(21, 1, 'Херсонская обл.');
INSERT INTO `tbl_region` VALUES(22, 1, 'Хмельницкая обл.');
INSERT INTO `tbl_region` VALUES(23, 1, 'Черкасская обл.');
INSERT INTO `tbl_region` VALUES(24, 1, 'Черниговская обл.');
INSERT INTO `tbl_region` VALUES(25, 1, 'Черновецкая обл.');
INSERT INTO `tbl_region` VALUES(27, 3, 'Минская обл.');
INSERT INTO `tbl_region` VALUES(28, 3, 'Брестская обл.');
INSERT INTO `tbl_region` VALUES(29, 3, 'Гомельская обл.');
INSERT INTO `tbl_region` VALUES(30, 3, 'Гродненская обл.');
INSERT INTO `tbl_region` VALUES(31, 3, 'Могилёвская обл.');
INSERT INTO `tbl_region` VALUES(32, 3, 'Витебская обл.');
INSERT INTO `tbl_region` VALUES(33, 4, 'Центральный округ');
INSERT INTO `tbl_region` VALUES(34, 4, 'Хайфский округ');
INSERT INTO `tbl_region` VALUES(35, 4, 'Северный округ');
INSERT INTO `tbl_region` VALUES(36, 4, 'Иерусалимский округ');
INSERT INTO `tbl_region` VALUES(37, 4, 'Южный округ');
INSERT INTO `tbl_region` VALUES(38, 4, 'Тель-Авивский округ');
INSERT INTO `tbl_region` VALUES(39, 4, 'Голанские высоты');
INSERT INTO `tbl_region` VALUES(40, 4, 'Западный берег');
INSERT INTO `tbl_region` VALUES(41, 4, 'Сектор Газа');
INSERT INTO `tbl_region` VALUES(42, 2, 'Адыгея');
INSERT INTO `tbl_region` VALUES(43, 2, 'Алтайский край');
INSERT INTO `tbl_region` VALUES(44, 2, 'Амурская обл.');
INSERT INTO `tbl_region` VALUES(45, 2, 'Архангельская обл.');
INSERT INTO `tbl_region` VALUES(46, 2, 'Астраханская обл.');
INSERT INTO `tbl_region` VALUES(47, 2, 'Башкортостан');
INSERT INTO `tbl_region` VALUES(48, 2, 'Белгородская обл.');
INSERT INTO `tbl_region` VALUES(49, 2, 'Брянская обл.');
INSERT INTO `tbl_region` VALUES(50, 2, 'Бурятия');
INSERT INTO `tbl_region` VALUES(51, 2, 'Владимирская обл.');
INSERT INTO `tbl_region` VALUES(52, 2, 'Волгоградская обл.');
INSERT INTO `tbl_region` VALUES(53, 2, 'Вологодская обл.');
INSERT INTO `tbl_region` VALUES(54, 2, 'Воронежская обл.');
INSERT INTO `tbl_region` VALUES(55, 2, 'Дагестан');
INSERT INTO `tbl_region` VALUES(56, 2, 'Еврейская АО');
INSERT INTO `tbl_region` VALUES(57, 2, 'Ивановская обл.');
INSERT INTO `tbl_region` VALUES(58, 2, 'Иркутская обл.');
INSERT INTO `tbl_region` VALUES(59, 2, 'Кабардино-Балкария');
INSERT INTO `tbl_region` VALUES(60, 2, 'Калининградская обл.');
INSERT INTO `tbl_region` VALUES(61, 2, 'Калмыкия');
INSERT INTO `tbl_region` VALUES(62, 2, 'Калужская обл.');
INSERT INTO `tbl_region` VALUES(63, 2, 'Камчатская обл.');
INSERT INTO `tbl_region` VALUES(64, 2, 'Карачаево-Черкессия');
INSERT INTO `tbl_region` VALUES(65, 2, 'Карелия');
INSERT INTO `tbl_region` VALUES(66, 2, 'Кемеровская обл.');
INSERT INTO `tbl_region` VALUES(67, 2, 'Кировская обл.');
INSERT INTO `tbl_region` VALUES(68, 2, 'Коми');
INSERT INTO `tbl_region` VALUES(69, 2, 'Корякский АО');
INSERT INTO `tbl_region` VALUES(70, 2, 'Костромская обл.');
INSERT INTO `tbl_region` VALUES(71, 2, 'Краснодарский край');
INSERT INTO `tbl_region` VALUES(72, 2, 'Красноярский край');
INSERT INTO `tbl_region` VALUES(73, 2, 'Курганская обл.');
INSERT INTO `tbl_region` VALUES(74, 2, 'Курская обл.');
INSERT INTO `tbl_region` VALUES(75, 2, 'Ленинградская обл.');
INSERT INTO `tbl_region` VALUES(76, 2, 'Липецкая обл.');
INSERT INTO `tbl_region` VALUES(77, 2, 'Магаданская обл.');
INSERT INTO `tbl_region` VALUES(78, 2, 'Марий Эл');
INSERT INTO `tbl_region` VALUES(79, 2, 'Мордовия');
INSERT INTO `tbl_region` VALUES(80, 2, 'Московская обл.');
INSERT INTO `tbl_region` VALUES(81, 2, 'Мурманская обл.');
INSERT INTO `tbl_region` VALUES(82, 2, 'Ненецкий АО');
INSERT INTO `tbl_region` VALUES(83, 2, 'Нижегородская обл.');
INSERT INTO `tbl_region` VALUES(84, 2, 'Новгородская обл.');
INSERT INTO `tbl_region` VALUES(85, 2, 'Новосибирская обл.');
INSERT INTO `tbl_region` VALUES(86, 2, 'Омская обл.');
INSERT INTO `tbl_region` VALUES(87, 2, 'Оренбургская обл.');
INSERT INTO `tbl_region` VALUES(88, 2, 'Орловская обл.');
INSERT INTO `tbl_region` VALUES(89, 2, 'Пензенская обл.');
INSERT INTO `tbl_region` VALUES(90, 2, 'Пермская обл.');
INSERT INTO `tbl_region` VALUES(91, 2, 'Приморский край');
INSERT INTO `tbl_region` VALUES(92, 2, 'Псковская обл.');
INSERT INTO `tbl_region` VALUES(93, 2, 'Республика Ингушетия');
INSERT INTO `tbl_region` VALUES(94, 2, 'Ростовская обл.');
INSERT INTO `tbl_region` VALUES(95, 2, 'Рязанская обл.');
INSERT INTO `tbl_region` VALUES(96, 2, 'Самарская обл.');
INSERT INTO `tbl_region` VALUES(97, 2, 'Саратовская обл.');
INSERT INTO `tbl_region` VALUES(98, 2, 'Саха (Якутия)');
INSERT INTO `tbl_region` VALUES(99, 2, 'Сахалинская обл.');
INSERT INTO `tbl_region` VALUES(100, 2, 'Свердловская обл.');
INSERT INTO `tbl_region` VALUES(101, 2, 'Северная Осетия');
INSERT INTO `tbl_region` VALUES(102, 2, 'Смоленская обл.');
INSERT INTO `tbl_region` VALUES(103, 2, 'Ставропольский край');
INSERT INTO `tbl_region` VALUES(104, 2, 'Таймырский АО');
INSERT INTO `tbl_region` VALUES(105, 2, 'Тамбовская обл.');
INSERT INTO `tbl_region` VALUES(106, 2, 'Татарстан');
INSERT INTO `tbl_region` VALUES(107, 2, 'Тверская обл.');
INSERT INTO `tbl_region` VALUES(108, 2, 'Томская обл.');
INSERT INTO `tbl_region` VALUES(109, 2, 'Тульская обл.');
INSERT INTO `tbl_region` VALUES(110, 2, 'Тыва');
INSERT INTO `tbl_region` VALUES(111, 2, 'Тюменская обл.');
INSERT INTO `tbl_region` VALUES(112, 2, 'Удмуртия');
INSERT INTO `tbl_region` VALUES(113, 2, 'Ульяновская обл.');
INSERT INTO `tbl_region` VALUES(114, 2, 'Хабаровский край');
INSERT INTO `tbl_region` VALUES(115, 2, 'Хакасия');
INSERT INTO `tbl_region` VALUES(116, 2, 'Ханты-Мансийский АО');
INSERT INTO `tbl_region` VALUES(117, 2, 'Челябинская обл.');
INSERT INTO `tbl_region` VALUES(118, 2, 'Чеченская Республика');
INSERT INTO `tbl_region` VALUES(119, 2, 'Читинская обл.');
INSERT INTO `tbl_region` VALUES(120, 2, 'Чувашия');
INSERT INTO `tbl_region` VALUES(121, 2, 'Чукотский АО');
INSERT INTO `tbl_region` VALUES(122, 2, 'Эвенкийский АО');
INSERT INTO `tbl_region` VALUES(123, 2, 'Ямало-Ненецкий АО');
INSERT INTO `tbl_region` VALUES(124, 2, 'Ярославская обл.');

CREATE TABLE `tbl_city` (
  `id_city` int(11) NOT NULL AUTO_INCREMENT,
  `id_region` int(11) NOT NULL,
  `city` varchar(32) NOT NULL,
  PRIMARY KEY (`id_city`),
  UNIQUE KEY `city` (`city`),
  KEY `id_region` (`id_region`)
);

INSERT INTO `tbl_city` VALUES(2, 4, 'Макеевка');
INSERT INTO `tbl_city` VALUES(6, 4, 'Енакиево');
INSERT INTO `tbl_city` VALUES(5, 4, 'Донецк');

Напишем следующий html код:

<select size="1" name="country" onchange="javascript:selectRegion();" style="float:left;">
<option value="">Все страны</option>
<optgroup label="Выберите страну">
<option value="3">Белоруссия</option>
<option value="4">Израиль</option>
<option value="2">Россия</option>
<option value="1">Украина</option>
</optgroup>
</select>

<div name="selectDataRegion" style="float:left;"></div>
<div name="selectDataCity" style="float:left;"></div>

Обратите внимание на 2 пустых дива «selectDataRegion» и «selectDataCity», в них мы будем вставлять сгенерированные выпадающие списки. Селект со странами сгенерирован с помощью запроса в базу с сортировкой по полю названия страны. Впринципе, если первый список у Вас статичный и данные неизменны, то можно просто написать данные в html и не использовать первую таблицу базы, но тогда прийдётся более внимательно относится к связке страны и value select соответственно.

ПОЛЕЗНО  Пишем правильный код на jQuery

Теперь напишем всего две функции JavaScript:

function selectRegion(){
        var id_country = $('select[name="country"]').val();
        if(!id_country){
                $('div[name="selectDataRegion"]').html('');
                $('div[name="selectDataCity"]').html('');
        }else{
                $.ajax({
                        type: "POST",
                        url: "/action/ajax.base.php",
                        data: { action: 'showRegionForInsert', id_country: id_country },
                        cache: false,
                        success: function(responce){ $('div[name="selectDataRegion"]').html(responce); }
                });
        };
};

function selectCity(){
        var id_region = $('select[name="region"]').val();
        $.ajax({
                type: "POST",
                url: "/action/ajax.base.php",
                data: { action: 'showCityForInsert', id_region: id_region },
                cache: false,
                success: function(responce){ $('div[name="selectDataCity"]').html(responce); }
        });
};

Обратите внимание, что в обеих функциях мы обращаемся к одному и тому же файлу. Красота пересылки данных методом POST позволяет создать переключатель switch по переменной action и правильно им воспользоваться.

Создадим файл исполнения /action/ajax.base.php:

<?php

ini_set(default_charset,"UTF-8");

# include data base
require "../../mysql.inc.php";

switch ($_POST['action']){

        case "showRegionForInsert":
                echo '<select size="1" name="region" onchange="javascript:selectCity();">';
                $rows = $DB->select('SELECT * FROM tbl_region WHERE id_country=? ORDER BY region ASC', $_POST['id_country']);
                foreach ($rows as $numRow => $row) {
                        echo '<option value="'.$row['id_region'].'">'.$row['region'].'</option>';
                };
                echo '</select>';
                break;

        case "showCityForInsert":
                echo '<select size="1" name="city">';
                $rows = $DB->select('SELECT * FROM tbl_city WHERE id_region=? ORDER BY city ASC', $_POST['id_region']);
                foreach ($rows as $numRow => $row) {
                        echo '<option value="'.$row['id_city'].'">'.$row['city'].'</option>';
                };
                echo '</select>';
                break;

};

?>

Осталось теперь протестировать. Выберите «Украина» -> «Донецкая обл.» -> «Город» и будет Вам счастье! В качестве домашнего задания можно реализовать так: не скрывать списки областей и городов, а вместо пустых дивов показывать те же выпадающие списки, только прописать в них один нулевой параметр с тескстом выбора данных в раннем селекте или манипулировать параметром «disabled».

ПОЛЕЗНО  Обернуть текст в список (bbcode)

источник + еще 1 статья по теме + еще 1 манул