Загрузка городов через AJAX

страна

регион

город

Как это реализовано
Для начала нам нужна база городов. Скачать базу в формате SQL можно здесь.

Клиентская часть

Посмотреть клиентский код можно здесь.
AJAX у меня реализован через известную библиотеку jQuery. При выборе страны (или региона) отсылаем серверному скрипту 2 параметра методом POST:
id — id объекта (страны или региона)
type — тип списка, который нужно получить (города или регионы)

<script type="text/javascript">
function getList(type, obj) {
    $("#loading_" + type).show(); // запускаем крутящееся колесико
    $.post("/ajax/city.php", {type: type, id: $("#"+obj).val()}, onAjaxSuccess);
    function onAjaxSuccess(data) {
         out = document.getElementById(type);
         for (var i = out.length - 1; i >= 0; i--) {
              out.options[i] = null;
         }
         eval(data);
         $("#loading_" + type).hide(); // скрываем крутящееся колесико
    }
}
</script>

Во время обращения к серверному скрипту следует оповестить пользователя о том, что идет какой-либо обмен данными (показываем крутящееся колесико загрузки). Индикация во время обмена данными в Web 2.0 играет очень важную роль: посетители могли еще не привыкнуть к таким способам обновления страницы.

Серверная часть

Как только на сервер пришел запрос от клиента (параметры id и type), мы формируем либо список регионов, либо список городов и отправляем его клиенту. Список формируется в виде js-объекта Option а затем, когда ответ пришел клиенту, то этот ответ (текстовая строка) выполняется через eval и получается полноценный html-код из option. Изначально я сразу формировал html-код из option, но в одном из браузеров (по-моему, IE6) он не хотел вставляться в select, поэтому пришлось использовать извращение eval().

<?php
header('Content-Type: text/html; charset=utf-8');
mysql_connect('localhost', 'пользователь', 'пароль' );
mysql_select_db('ваша_база');
mysql_query('set names utf8');

$id   = (int)$_POST['id']; // id объекта (страна или регион)
$type = $_POST['type']; // тип списка, который нужно получить (города или регионы)

sleep(1); // спешить нам некуда

if ($type == 'city') {
	// выбираем города в данном регионе
	$result = mysql_query('SELECT *
    	                   FROM city
    	                   WHERE region_id = '.$id.'
    	                   ORDER BY name');
	if (!empty($result)) {
		echo "out.options[out.options.length] = new Option('выберите город...','none');\n";
		while ($city = mysql_fetch_array($result)) {
			echo "out.options[out.options.length] = new Option('".$city['name']."','".$city['city_id']."');\n";
		}
	}
	else {
		echo "out.options[out.options.length] = new Option('нет городов','none');\n";
	}
}
if ($type == 'region') {
	// выбираем регионы в данной стране
	$result = mysql_query('SELECT *
    	                    FROM region
    	                    WHERE country_id = '.$id.'
    	                    ORDER BY name');
	if (!empty($result)) {
		echo "out.options[out.options.length] = new Option('выберите регион...','none');\n";
		while ($region = mysql_fetch_array($result)) {
			echo "out.options[out.options.length] = new Option('".$region['name']."','".$region['region_id']."');\n";
		}
	}
	else {
		echo "out.options[out.options.length] = new Option('нет регионов','none');\n";
	}
}
?>

sleep(1) — замораживает выполнение скрипта на 1 секунду. Я это сделал специально, чтобы можно было увидеть крутящееся колесико при загрузке.

Исходники

1) для начала необходимо установить базу данных. Скачать базу городов (формат sql, 100 Кб)
2) скачать исходные коды
структура от корня должна быть такой
/city.html — файл с формой выбора (клиентская часть)
/ajax/city.php — файл, который обращается в базу (серверная часть)
/jquery.js — библиотека jquery для работы с ajax
/img/ajax_loader.gif — колесико загрузки

Проблемы с кодировкой

Итак, у многих возникли крокозябры, поэтому расскажу про решение данной проблемы в целом.

Данная проблема может возникать по нескольким причинам
1) скрипты не в кодировке utf-8
для этого нужно сохранять файлы только в utf-8
это можно сделать в любом современном редакторе
вот например, PHP Expert Editor

или вот даже в блокноте (если делать совсем через жопу)

2) сервер должен отдавать файлы в utf-8
для этого в самом начале скрипта пишем

<?php
header('Content-Type: text/html; charset=utf-8');
...ну а дальше идет основной код скрипта...

еще бы неплохо прописать в html-коде (в самом верху в секции head) метатег

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
...
</head>

3) база должна быть в utf-8
это можно посмотреть через phpMyAdmin

еще не всегда по умолчанию MySQL устанавливает правильную (читай utf-8) кодировку при запросе к базе
самое простое решение в лоб — это после подключения к базе сделать 1 запрос 'set names utf8'

<?php
mysql_connect('localhost', 'пользователь', 'пароль' );
mysql_select_db('ваша_база');
// установили кодировку
mysql_query('set names utf8');

// а теперь делаем какой-нибудь запрос
$result = mysql_query('SELECT * FROM city  ORDER BY name');
// и еще потом ниже что-то получаем
$result2 = mysql_query('SELECT * FROM region');
?>
#1

имели ли опыт с JSON ?
используя json, в этом примере код можно сделать менее избыточным

sikoshi, 25.04.2010 - 18:28
#2

Да, с JSON имел опыт. Но этот код писался в 2008 году, поэтому переделывать его влом:)
Кстати, JSON активно используется в Контакте.

admin, 25.04.2010 - 19:33
#3

Вконтакте не очень :)
на такие запросы они выдают готовый html, как я понимаю, закешированный в nginx

sikoshi, 25.04.2010 - 19:51
#4

Поиск людей, список друзей в Контакте сделан через JSON. По-моему даже API у них выдает списки в виде json.

admin, 25.04.2010 - 22:28
#5

Спасибо, Серёж!

Полезный у тебя сайт.
Последнее время часто натыкаюсь.

Апрель, 6.06.2010 - 21:14
#6

А нельзя выложить готовые скрипты в архиве как это было сделано с sql базой городов?

KuzyaK, 24.06.2010 - 12:50
#7

Да, где конфиг, где db.php

onthebest, 26.06.2010 - 21:38
#8

Что такое out.options[out.options.length] и где функция Option ? В каком виде должен содержаться ответ от city.php ?
в нем должен быть уже html список ?

Евгений, 9.07.2010 - 10:54
#9

Разобрался. Но така как не было библиотеки DB то воспользовался штатными средствами cms для запросов. потом еще были проблемы с кодировкой, т.к. сайт в windows-1251, но помог $id=iconv("UTF-8", "windows-1251", $id); в city.php

Евгений, 9.07.2010 - 11:50
#10

http://blog.wel.org.ua/index.php/2010/08/есть-где-взять-названия-городов-насел/

Для выборки данных из базы я использовал PDO, у автора пример с DBSimple.
Жду предложений

Валера, 30.08.2010 - 14:19
#11

Не могли бы вы выложить/отослать эти скрипты в архиве. У меня до конца все получается с клиентской частью.

Заранее спасибо!

adetpps, 15.11.2010 - 13:44
#12

Огромное человеческое спасибо за базу и скрипты!
сразу заработали. ща буду переделывать под свой сайт :)
Удачи и дальнейших вдохновений!

Юрий, 17.11.2010 - 06:25
#13

Извините, я совсем недавно стал учить php и я вот не могу понять, что это за строчки:
include($_SERVER['DOCUMENT_ROOT'].'/include/config.php');
include($_SERVER['DOCUMENT_ROOT'].'/include/db.php');

И где указаны название БД, из которой будем брать страны, регионы и города?

Ketsyki, 21.11.2010 - 18:40
#14

Отошлите пожалуйста и мне эти скрипты в архиве.
Тоже проблема с клиентской частью.

Дима, 22.11.2010 - 01:19
#15

Не работает
echo "out.options[out.options.length] = new Option('".$city['name']."','".$city['city_id']."');\n";
Хотя из базы данные приходят

Дима, 22.11.2010 - 11:52
#16

Реально, скиньте, пожалуйста, архив с всеми скриптами и библиотеками, чтоб распаковал его, и все работает. А то у меня и без того подключена куча библиотек в которых я еще почти не разобрался, а с этой DBSimple начинаются траблы :)

Ketsyki, 22.11.2010 - 15:55
#17

Непонятно что писать в options html

вот тут какой код должен быть чтоб показывало регионы?

Plexx, 21.01.2011 - 13:07
#18

Огромная просьба, скиньте мне тоже скрипты в архиве. Потому что, не могу до конца разобраться что да как. А ваш скрипт очень нужная весчь, давно ее ищу.

Антон, 27.05.2011 - 14:49
#19

Если можно и мне пожалуйста скиньте скрипты, буду очень признателен)

Painbot, 6.06.2011 - 21:14
#20

Мне тоже интересно! а..где..:
??? и где его вставить???

Евгений, 17.06.2011 - 18:26
#21

Скиньте мне тоже клиентскую часть, а то писать что-то лень)))

panda, 24.06.2011 - 08:25
#22

Комменты ваще писец)

Лень читать доки - вон из профессии!

SofTie, 12.08.2011 - 16:20
#23

Даже если не лень читать просто с готовым решением проще разбираться.
Я пока понял даже не понял что в какой файл.
Что например имеется ввиду серверная часть.
db.php вроде подключение к базе, или я ошибаюсь.
config.php что тогда тут за конфиг
Я крутил, вертел и ничего не понял!!!
Толи я чет не до понимаю либо...

SASA, 23.08.2011 - 18:17
#24

Выложите еще и сам скрипт
Заранее спасибо...

SASA, 23.08.2011 - 18:23
#25

В чужом скрипте всегда лень ковыряться, уже бы выложил весь скрипт, а там мне например легче свой целиком написать)))))

Алашка, 17.09.2011 - 11:11
#26

Напишите как выводится сам html код. Т.е сами селекты как встраиваются в форму...

Mikluha, 16.12.2011 - 14:37
#27

Если можно скиньте и мне пожалуйста все скрипты. Спасибо

аноним, 17.12.2011 - 13:52
#28

И мне можно тоже скрипты! пожалуйста-пожалуйста!)

Ника, 23.12.2011 - 06:18
#29

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

Максим, 14.01.2012 - 09:52
#30

Аналогичная проблема. Не разобрался DbSimple . просьба прислать готовое решение для сервера в архиве . ну или хотя б эти проклятые db.php а config.php я нашел.да только подключение к базе данных осущ. $DB = DbSimple_Generic::connect("mysql://brendbar_l2cri:memento@localhost/brendbar_bd");

l2cri, 2.02.2012 - 12:30
#31

Больше в рунете не могу более лучшего решения найти.
Никак не могу разобраться с клиентской частью, пожалуйста скиньте весь архив со скриптом.
Спасибо!

Виктор, 25.04.2012 - 12:58
#32

УБОЙНАЯ ШТУКА!!!!! ток скриптик бы полный мне бы тож....а? Ну пожалуйстааааааааа........ Пипец как надо такую штуку мне на сайт воткнуть!! буду ждать надеятся и верить...

грех, 29.04.2012 - 05:51
#33

Спасибо тебе чувак! спас меня от расстрела!

Юля, 12.05.2012 - 16:29
#34

можешь мне тоже скинуть на мыло архив ? Или хотя бы тут выложи ссылкой чтобы все не просили а качали :). Скинь пожалуйста весь архив со всеми делами ?

Мурад, 19.05.2012 - 00:53
#35

а можно количество селектов увеличить ? Например мне нужно 5 или даже 6

Мурад, 19.05.2012 - 00:56
#36

ПОСОНЫ! ОБНОВИЛ СТАТЬЮ.
убрал DbSimple и выложил исходники.

admin, 19.06.2012 - 09:16
#37

Уважаемый, спасибо, только что делать, если у вас тект в ANSI, а у меня база в UTF-8.
Все прекрасно работает, только значения приходят пустые?
Спасибо!

Сережа, 22.07.2012 - 15:39
#38

А, все, просто я нуб.
поправил через notepad++
Вы гений!

Сережа, 22.07.2012 - 15:43
#39

висит на загрузке ...
БД пробовал открывать руками - работает .

Андрей, 23.07.2012 - 15:13
#40

значит возникает ошибка в скрипте аякса и данные не отсылаются в браузер.

как выявить ошибку
1) смотри логи
2) установи расширение firebug для Firefox. там виден каждый запрос и что возвращается в ответе.
3) метод в тупую.
открой скрипт /ajax/city.php и замени в нем
$id = (int)$_POST['id'];
$type = $_POST['type'];
на
$id = 3827;
$type = 'city';

далее открой в новом окне аяксовый скрипт /ajax/city.php
скорее всего увидишь ошибку.

admin, 23.07.2012 - 15:51
#41

чудеса ...
куда name делся ?
out.options[out.options.length] = new Option('','3839');

Андрей, 23.07.2012 - 16:47
#42

выведи просто
echo $city['name'];

admin, 23.07.2012 - 16:57
#43

не видит .
где-то в кодировке траблы
mysql_query('set names utf8_general_ci');
mysql_query('set names utf8');

Андрей, 23.07.2012 - 17:13
#44

перезалил в utf
http://ekimoff.ru/download/db_city/city2.zip
удали таблицы и снова сделай импорт.

admin, 23.07.2012 - 17:24
#45

регионы получаю
городов нет

Андрей, 23.07.2012 - 17:42
#46

страна -> регион выводит список
после выбора региона городов нет

Андрей, 23.07.2012 - 18:08
#47

Ошибка найдена .
Огромное спасибо за скрипт и поддержку !

Андрей, 23.07.2012 - 21:39
#48

У меня не выводит даже регионы.
Андрей, в чем была ошибка?

YOBA, 24.07.2012 - 15:37
#49

@YOBA
методом научного тыка :)

проверь сначала
1. правильность путей
2. БД

--
@admin
"Куда - Откуда"
есть какое-либо простое решение ?

Андрей, 25.07.2012 - 06:51
#50

@Андрей
Сам все починил через 5 минут после отправки предыдущего поста, немного редактируя код вместо двоеточия поставил точку с запятой :)

Но теперь возникла другая проблема: Предположим, я выбрал область, появился список городов этой области. Так вот, нужно, чтобы в параметре value вместо city_id загружались элементы столбца name из таблицы city. Так вот, как это сделать? Помогите школьнику, целый день голову ломаю)

YOBA, 26.07.2012 - 23:43
#51

@YOBA

вместо
echo "out.options[out.options.length] = new Option('".$city['name']."','".$city['city_id']."');\n";

echo "out.options[out.options.length] = new Option('".$city['city_id']."','".$city['city_id']."');\n";

это имели ввиду ?

Андрей, 27.07.2012 - 04:09
#52

"Куда - Откуда"

удвояем скрипт , второй части дописываем всюду 2

:)

Андрей, 15.08.2012 - 11:17
#53

Здравствуйте! Большое спасибо за статью! Сейчас создаю сайт, и очень необходим зависимый список страны-области-города. Скажите пожалуйста, какой код нужно вставить на сайт, чтобы это работало? Заранее спасибо за помощь!

Влад, 2.10.2012 - 23:20
#54

Не получается использовать скрипт с новой библиотекой jquery, а от неё никак не отказаться.

User 1, 13.10.2012 - 23:18
#55

#54 - решено с помощью jQuery.noConflict();

User 1, 15.10.2012 - 23:54
#56

Последовал вашему примеру. Убрал только страну.

Областя выводит и записывает значения. А вот когда выбираю город и пытаюсь его потом в базу записать, оно записывать пустое значение в базу.

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

Помогите

Denys, 9.12.2012 - 13:03
#57

Для индикации AJAX операций можно также воспользоваться маленькой JavaScript библиотекой AjaxLoader.js (http://musicvano.github.io/ajaxloader/) для генерации спиннеров и динамического изменения их параметров.

Иван, 27.04.2013 - 21:41
#58

как сделать чтобы при изменении страны сбрасывались данные города - потому как некорректно выбрать одну страну , а потом другую - а города будут показываться предыдущей

Woody, 2.11.2013 - 13:25
#59

методом тыка (не силен в ява) сделал так (в коде запроса аякса) :)
if(type=="region")
{
out1 = document.getElementById('city');
for (var i = out1.length - 1; i >= 0;i--)
{out1.options[i] = null;}
}

Woody, 2.11.2013 - 13:39
#60

спасибо за отличный, удобный и простой код!

Woody, 3.11.2013 - 15:19
#61

Все работает прекрасно, но не пойму как вывести city_id ?
Присваиваю переменные aStrana, aRegion, aCity в сессии.
$_SESSION['aCity'] = $_POST['city_id']

Вывожу значения переменных $_SESSION.
Array (
[aStrana] => 177
[aRegion] => 207
[aCity] => undefined )

alex_bog, 15.11.2013 - 09:30
#62

Можно удалить пост #61.
Запутался в скриптах. Экспериментировал на двух скриптах одновременно :(

alex_bog, 15.11.2013 - 10:50
#63

Подскажите пожалуйста, как удвоить чтобы было так:
(откуда СТРАНА-РЕГИОН-ГОРОД куда СТРАНА-РЕГИОН-ГОРОД)

saha, 6.12.2013 - 13:32
#64

1) создать копию функции getList, только назвать ее getList2 и поменять $('#loading_' на $('#loading2_'

function getList2(type, obj) {
$('#loading2_' + type).show();
$.post('/ajax/city.php', {type: type, id: $('#'+obj).val()}, onAjaxSuccess);
function onAjaxSuccess(data) {
out = document.getElementById(type);
for (var i = out.length - 1; i >= 0; i--) {
out.options[i] = null;
}
eval(data);
$('#loading2_' + type).hide();
}
}

2) создать копию html-формы http://ekimoff.ru/city.html
только заменить select на вид
select name="countryid2" id="country2" onchange="getList2('region2', 'country2')"

и не забыть добавить двойку к id колесика загрузки
div style="display: none" id="loading2_region"...Загрузка...

цифру 2 можно заменить на что-то более осмысленное, типа getListTo - куда.

admin, 6.12.2013 - 15:56
#65

Добрый день , всё сделал как написано выше, но второй выбор всё ровно не работает, при выборе страны колёсик крутится, а поле выбора региона - пустое. Подскажите, пожалуйста, в чём может быть дело?! Заранее спасибо!

saha, 9.12.2013 - 17:45
#66

Помогите пожалуйста,делаю так, всё равно не получается, в чём ошибка?
function getList2(type, obj) {
$('#loading_2' + type).show();
$.post('/ajax/city.php', {type: type, id: $('#'+obj).val()}, onAjaxSuccess);
function onAjaxSuccess(data) {
out = document.getElementById(type);
for (var i = out.length - 1; i >= 0; i--) {
out.options[i] = null;
}
eval(data);
$('#loading_2' + type).hide();
}
}

Пример загрузки городов через AJAX

страна

Туркмения
Туркс и Кейкос
город
Загрузка...

saha, 2.01.2014 - 13:56
#67

Скажите пожалуйста, а как можно реализовать автоподстановку улицы в этом примере?

Александр, 23.06.2015 - 02:50
#68

День добрый! можно мне тоже на мыло выслать все решение? Спасибо!

AXL, 22.10.2015 - 08:22
#69

А внутри модуля джумлы, если формы и скрипт в дефаулт пхп разместить, будет работать? Почему -то не появляется картинка загрузки и не загружаются регионы, с чем это может быть связано?

Nicks, 25.03.2016 - 18:47
#70

Добрый день!
Как можно подтянуть список стран сразу из базы данных чтобы не использовать то, что вписано в HTML страницу?
Слишком огромный код получается

Ден, 17.11.2016 - 13:23
Оставить комментарий