Выбор страна, регион, город с использованием Ajax

Имеются три селекта: страна>регион>город при выборе страны подгружается список регионов, при выборе региона подгружается список городов, а при выборе города нужно делать переход на страницу index.php?country=RU&region=2&city=3 то есть с теми значениями, которые были выбраны в трёх селектах, по ним нужно сделать переход на страницу.

Более простой пример, не использующий технологию Ajax: выбор страна, регион, город, но без технологии Ajax, с перезагрузкой страницы после выбора каждого селекта.

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


Ваш IP: 3.239.97.34. Информация об IP адресе 3.239.97.34



Код примера специально упрощен с сохранением работоспособности. В приведенном примере не реализованны функции подсветки столицы страны и областного центра. Нет определения текущего города по ip.

На этапе наполнения базы я написал дополнительную систему, которая разбирает ответы от сервисов Чей IP адрес? (Whois-сервис для ip) и чтобы не быть заблокированными за частые обращения все сохраняет в базу данных.

<?
/* Выбор страна, регион, город с использованием технологии Ajax
Взято http://htmlweb.ru/ajax/example/region.php
Разрешается использование в любых своих разработках.
Размешение кода в открытом доступе разрешается только с сохранением активной ссылки на источник.
Все остальные права принадлежат Колесникову Дмитрию Геннадьевичу.
Полная платная версия с базой доступна здесь: http://htmlweb.ru/geo/geo_pay.php.
*/

//include_once $_SERVER['DOCUMENT_ROOT'].'/geo/geo.inc.php';
// подключаемся к БД
$link = mysqli_connect(HostName, UserName, Password, DBName);


if(!isset($geo))$geo = new Geo(); // запускаем класс без параметров
if($geo->ip=='127.0.0.1') $geo->get_for_ip('93.178.64.171'); // для отладки
echo "<br>\nВаш IP: <b>".$geo->ip."</b>. <a href=\"/analiz/whois_ip.php?ip=".$geo->ip."\">Информация об IP адресе ".$geo->ip."</a><br> <br>";

define("db_prefix","geo_");

function sql($query) {
    global $link;
    $res=mysqli_query ( $link, $query );
    if(!$res)die("Запрос:\n".$query."\n");
    return $res;
}

// capital - ссылка на город столица государства
// telcod - телефонный код страны
sql('CREATE TABLE IF NOT EXISTS '.db_prefix.'country (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT,
        PRIMARY KEY ( id ),
        name VARCHAR(64) NOT NULL UNIQUE,
        fullname VARCHAR(64) NOT NULL,
        english VARCHAR(64) NOT NULL,
        country_code2 CHAR(2) NOT NULL,
        country_code3 CHAR(3) NOT NULL,
        iso CHAR(3) NOT NULL,
        telcod CHAR(4) NOT NULL,
        location ENUM("Азия", "Океания", "Европа", "Африка", "Антарктика", "Америка"),
        capital INT UNSIGNED NOT NULL
        ) DEFAULT CHARACTER SET utf8 COLLATE utf8_bin');

// capital - ссылка на город, обласной центр
sql('CREATE TABLE IF NOT EXISTS '.db_prefix.'area (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT,
        PRIMARY KEY ( id ),
        name VARCHAR(64) NOT NULL,
        okrug VARCHAR(64) NOT NULL,
        country INT UNSIGNED NOT NULL,
        autocod VARCHAR(12) NOT NULL,
        capital INT UNSIGNED NOT NULL
        ) DEFAULT CHARACTER SET utf8 COLLATE utf8_bin');

sql('CREATE TABLE IF NOT EXISTS '.db_prefix.'city (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT,
        PRIMARY KEY ( id ),
        name VARCHAR(64) NOT NULL,
        area INT UNSIGNED NOT NULL,
        telcod CHAR(7) NOT NULL,
        latitude FLOAT(10,6),
        longitude FLOAT(10,6),
        country INT UNSIGNED NOT NULL
        ) DEFAULT CHARACTER SET utf8 COLLATE utf8_bin');

// ip - начальный IP адрес диапазона ip-адресов одного провайдера
// count - количество ip-адресов
sql('CREATE TABLE IF NOT EXISTS '.db_prefix.'geo_ip (
        ip1 BIGINT UNSIGNED NOT NULL UNIQUE,
        ip2 BIGINT UNSIGNED NOT NULL UNIQUE,
        city INT UNSIGNED NOT NULL,
        country INT UNSIGNED NOT NULL,
        upd DATETIME COMMENT "актуальность"
        ) DEFAULT CHARACTER SET utf8 COLLATE utf8_bin');

if(isset($_GET['country'])){
   echo "<option value='0'>выбрать регион</option>\n";
   $res = sql('SELECT * FROM '.db_prefix.'area WHERE country="'.addslashes(param('country')).'"');
   while($row = mysqli_fetch_assoc($res))
    echo "<option value='" . $row['id'] . "'>" . $row['name'] . "</option>\n";
   die;
   }
elseif(isset($_GET['region'])){
   echo "<option value='0'>выбрать город</option>\n";
   $res = sql('SELECT * FROM '.db_prefix.'city WHERE area="'.addslashes(param('region')).'"');
    while($row = mysqli_fetch_assoc($res))
    echo "<option value='" . $row['id'] . "'>" . $row['name'] . "</option>\n";
   die;
   }
elseif(isset($_GET['city'])){
$city=addslashes(param('city')); LoadGeo();
echo "
<div style='border: #C5D3DC 1px solid; padding: 10px; width: 97%;'>
Город: <b>". $geo['city']."</b><br>
Регион: <b>". $geo['region']."</b><br>
Округ: <b>". $geo['okrug']."</b><br>
Страна: <b>".$geo['country']."</b><br>
Код страны: <b>".$geo['country_code2']."</b><br>
Код страны: <b>".$geo['country_code3']."</b><br>
Широта: <b>".$geo['latitude']."</b><br>
Долгота: <b>".$geo['longitude']."</b><br>
Телефонный код страны:<b>".$geo['telcod']."</b><br>
Телефонный код города:<b>".$geo['country_telcod']."</b><br>
Автомобильный код региона:<b>".$geo['autocod']."</b><br>
Столица <b>". $geo['capital']."</b><br>
Областной центр <b>". $geo['capital']."</b><br>
</div>";
   die;
}

function LoadGeo() // для $city заполняет $geo и переменные $region, $country
{global $link, $geo, $city, $region, $country;
   $res = sql('SELECT * FROM '.db_prefix.'city WHERE id='.$city.' LIMIT 1');
   if($row = mysqli_fetch_assoc($res)){
     $geo['city']=$row['name'];
     $region=$row['area'];
     $geo['city_telcod']=$row['telcod'];
     $geo['latitude']=$row['latitude'];
     $geo['longitude']=$row['longitude'];
     $res = sql('SELECT * FROM '.db_prefix.'area WHERE id='.$region.' LIMIT 1');
     if($row = mysqli_fetch_assoc($res)){
    $geo['region']=$row['name'];
    $geo['okrug']=$row['okrug'];
    $geo['autocod']=$row['autocod'];
    $country=$row['country'];
    $res = sql('SELECT * FROM '.db_prefix.'country WHERE id='.$country.' LIMIT 1');
    if($row = mysqli_fetch_assoc($res)){
       $geo['country']=$row['name'];
       $geo['fullname']=$row['fullname'];
       $geo['english']=$row['english'];
       $geo['country_code2']=$row['country_code2'];
       $geo['country_code3']=$row['country_code3'];
       $geo['iso']=$row['iso'];
       $geo['country_telcod']=$row['telcod'];
       $geo['location']=$row['location'];
       }
    }
     }
}

$ip2=ip2long(getenv('REMOTE_ADDR'));
$f_add=false;
if($ip2>0){
   $res = sql('SELECT * FROM '.db_prefix.'geo_ip where '.$ip2.' BETWEEN ip1 and ip2 LIMIT 1');
   if ($geo = mysqli_fetch_assoc($res)) {$city=$geo['city']; LoadGeo();}
   }
?>
<br><br>
<select name="country" id="country" onLoad="this.focus = false;"
    onChange="ajaxLoad('region', '/ajax/example/region.php?country='+this.options[this.selectedIndex].value, '','',''); document.getElementById('region').disabled='';">
<option value="0">выбрать страну</option>

<?
$res = sql('SELECT * FROM '.db_prefix.'country');
while($row = mysqli_fetch_assoc($res)){
   if($row['id']==@$country){
        echo "<option value='" . $row['id'] . "' selected='selected'>" . $row['name'] . "</option>\n";
        $country=$row['id'];}
   else
        echo "<option value='" . $row['id'] . "'>" . $row['name'] . "</option>\n";
}
?>
</select>

<select name="region" id="region" <?=(@$country?'':'disabled="disabled"')?>
    onChange="ajaxLoad('city', '/ajax/example/region.php?region='+this.options[this.selectedIndex].value, '','',''); document.getElementById('city').disabled='';">
<option value="0" disabled="disabled">выбрать регион</option>
<?
if(@$country){
$res = sql('SELECT * FROM '.db_prefix.'area WHERE country='.$country);
while($row = mysqli_fetch_assoc($res)){
   if($row['id']==@$region){
        echo "<option value='" . $row['id'] . "' selected='selected'>" . $row['name'] . "</option>\n";
        $region=$row['id'];}
   else
        echo "<option value='" . $row['id'] . "'>" . $row['name'] . "</option>\n";
   }
}
?>

</select>


<select name="city" id="city" <?=(@$region?'':'disabled="disabled"')?>
    onChange="ajaxLoad('info', '/ajax/example/region.php?city='+this.options[this.selectedIndex].value, '','','');">

<option value="0" disabled="disabled">выбрать город</option>

<?
if(@$region){
$res = sql('SELECT * FROM '.db_prefix.'city WHERE area='.$region);
while($row = mysqli_fetch_assoc($res)){
   if($row['id']==@$city){
        echo "<option value='" . $row['id'] . "' selected='selected'>" . $row['name'] . "</option>\n";
        $region=$row['id'];}
   else
        echo "<option value='" . $row['id'] . "'>" . $row['name'] . "</option>\n";
   }
}
?>
</select>

<div id="info">
</div>

<script type="text/javascript">
function ajaxLoad(obj,url,defMessage,post,callback){
  var ajaxObj;
  if (defMessage) document.getElementById(obj).innerHTML=defMessage;
  if(window.XMLHttpRequest){
      ajaxObj = new XMLHttpRequest();
  } else if(window.ActiveXObject){
      ajaxObj = new ActiveXObject("Microsoft.XMLHTTP");
  } else {
      return;
  }
  ajaxObj.open ((post?'POST':'GET'), url);
  if (post&&ajaxObj.setRequestHeader)
      ajaxObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=windows-1251;");

  ajaxObj.onreadystatechange = ajaxCallBack(obj,ajaxObj,(callback?callback:null));
  ajaxObj.send(post);
  return false;
  }
function updateObj(obj, data, bold, blink){
   if(bold)data=data.bold();
   if(blink)data=data.blink();
   document.getElementById(obj).innerHTML = data; // упрощенный вариант, работает не во всех браузерах
  }
function ajaxCallBack(obj, ajaxObj, callback){
return function(){
    if(ajaxObj.readyState == 4){
       if(callback) if(!callback(obj,ajaxObj))return;
       if (ajaxObj.status==200)
        updateObj(obj, ajaxObj.responseText);
       else updateObj(obj, ajaxObj.status+' '+ajaxObj.statusText,1,1);
    }
}}

</script>

Тестовый дамп базы можете взять здесь.

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

Скрипт выбора страна, регион, город на чистом javascript с использованием нашего API

Если у вас мало запросов и Вы не хотите приобретать базу вы можете воспользоваться нашим API и следующим скриптом, который сделан на чистом javascript и не использует никаких дополнительных библиотек:

Код скрипта:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="Author" content="Kolesnikov D.G." />
    <title>Выбор страна, регион, город на чистом javascript</title>
</head>

<body style="max-width: 600px">
<h1>Выбор страна регион город на чистом javascript</h1>

<form method="get" action="#" onsubmit="return false;">
    <label style='font-size:15px;text-align: left'><b>Страна:</b>
        <select name="country" onChange="citySelect.loadArea(this)" onfocus="if(this.length<2)citySelect.loadCountry(this)">
            <option value="0">выбрать страну</option>
        </select>
    </label>
    <label>
        <select name="area" onChange="citySelect.loadCity(this)">
            <option value="0" disabled="disabled">выбрать регион</option>
        </select>
    </label>
    <label>
        <select name="city" onChange="citySelect.onCity(this)">
            <option value="0" disabled="disabled">выбрать город</option>
        </select>
    </label>
</form>
<br>

<div id="info"></div>

<script type="text/javascript">
var citySelect = {
    api_url:/* https://htmlweb.ru */ '/json/geo/',
    api_key:'',//&api_key=NNNN', // взять здесь: https://htmlweb.ru/user/#allowDomain
    loadCountry: function (t) {
        t.innerHTML='<option value="0" disabled>загружаю список стран</option>';
        fetch(this.api_url+'country_list?format=short'+this.api_key)
            .then(
                function (data) { // обрабатываем ответ от сервера
                    if (data.status !== 200) {
                        return Promise.reject(new Error(data.statusText));
                    }
                    return data.json(); // раскодируем json в объект
                })
            .then(
                function (data) {
                    console.log('data:', data);
                    document.getElementById('info').innerHTML = '';
                    if (data.limit) console.log('Осталось ' + data.limit + ' запросов');
                    if (data.error) document.getElementById('info').innerHTML = '<div style="color:red">'+data.error+'</div>';
                    else if (data.message) document.getElementById('info').innerHTML = data.message;
                    if (data.items){
                        t.innerHTML='<option value="0" disabled>выбрать страну</option>';
                        for (var i in data.items) {
                            var option = document.createElement("option");
                            option.setAttribute("value", i);
                            option.text = data.items[i];
                            t.appendChild(option);
                        }
                    }
                    t.form.area.disabled = false;
                    t.form.city.disabled = true;
                })
            .catch(
                function (error) {
                    console.error(error)
                });
    },
    loadArea: function (t) { // t = country
        t.form.area.innerHTML='<option value="0" disabled>загружаю список регионов</option>';
        fetch(this.api_url+'area_list?format=short'+this.api_key+'&country='+t.options[t.selectedIndex].value)
            .then(
                function (data) { // обрабатываем ответ от сервера
                    if (data.status !== 200) {
                        return Promise.reject(new Error(data.statusText));
                    }
                    return data.json(); // раскодируем json в объект
                })
            .then(
                function (data) {
                    console.log('data:', data);
                    document.getElementById('info').innerHTML = '';
                    if (data.limit) console.log('Осталось ' + data.limit + ' запросов');
                    if (data.error) document.getElementById('info').innerHTML = '<div style="color:red">'+data.error+'</div>';
                    else if (data.message) document.getElementById('info').innerHTML = data.message;
                    if (data.items){
                        t.form.area.innerHTML='<option value="0" disabled>выбрать регион</option>';
                        for (var i in data.items) {
                            var option = document.createElement("option");
                            option.setAttribute("value", i);
                            option.text = data.items[i];
                            t.form.area.appendChild(option);
                        }
                    }else{
                        t.form.area.disabled = true;
                        this.loadCity(t); // регионов нет, - загрузить города
                    }
                })
            .catch(
                function (error) {
                    console.error(error)
                });
    },
    loadCity: function (t) { // t = area
        t.form.city.innerHTML='<option value="0" disabled>загружаю список городов</option>';
        fetch(this.api_url+'city_list?format=short'+this.api_key+'&country='+t.form.country.options[t.form.country.selectedIndex].value+'&area='+t.options[t.selectedIndex].value)
            .then(
                function (data) { // обрабатываем ответ от сервера
                    if (data.status !== 200) {
                        return Promise.reject(new Error(data.statusText));
                    }
                    return data.json(); // раскодируем json в объект
                })
            .then(
                function (data) {
                    console.log('data:', data);
                    document.getElementById('info').innerHTML = '';
                    if (data.limit) console.log('Осталось ' + data.limit + ' запросов');
                    if (data.error) document.getElementById('info').innerHTML = '<div style="color: red">'+data.error+'</div>';
                    else if (data.message) document.getElementById('info').innerHTML = data.message;
                    if (data.items){
                        t.form.city.innerHTML='<option value="0" disabled>выбрать город</option>';
                        for (var i in data.items) {
                            var option = document.createElement("option");
                            option.setAttribute("value", i);
                            option.text = data.items[i];
                            t.form.city.appendChild(option);
                        }
                        t.form.city.disabled = false;
                    }
                })
            .catch(
                function (error) {
                    console.error(error)
                });
    },
    onCity: function (t) {
        fetch(this.api_url+'/city?city=' + t.options[t.selectedIndex].value+this.api_key)
            .then(
                function (data) { // обрабатываем ответ от сервера
                    if (data.status !== 200) {
                        return Promise.reject(new Error(data.statusText));
                    }
                    return data.json(); // раскодируем json в объект
                })
            .then(
                function (data) {
                    console.log('data:', data);
                    document.getElementById('info').innerHTML = data;
                })
            .catch(
                function (error) {
                    console.error(error)
                });
    }
}
</script>
<br><br><p>Скрипт скачан здесь: <a href="https://htmlweb.ru/ajax/example/region.php">https://htmlweb.ru/ajax/example/region.php</a>
</body>
</html>

Чтобы сэкономить ваши обращения к нашему API загрузка производиться после клика в поле страна, все остальные поля заблокированы и также будут загружаться по мере выбора данных в предыдущих.

Открыть пример, скачать архив

Чтобы использовать данный пример у себя на сайте необходимо:

  1. Зарегистрироваться или Войти
  2. Строку
    api_key:'',//&api_key=NNNN', // взять здесь: https://htmlweb.ru/user/#allowDomain
    заменить на
    api_key:'&api_key=КОД_ИЗ_ЛК', // взять здесь: https://htmlweb.ru/user/#allowDomain
  3. Прописать домен с которого вы будете обращаться на странице: https://htmlweb.ru/user/#allowDomain
  4. Пополнить баланс

Вы можете в своих разработках использовать API выбора города из списка по начальным буквам.


.