Наш чат в Telegram для обмена идеями, проектами, мыслями, людьми в сфере ИТ г.Ростова-на-Дону: @it_rostov

API Выбор города из выпадающего списка по начальным буквам (API+Ajax)

Работу примера можно увидеть здесь

Большинство API функций сервиса htmlweb.ru совершенно бесплатны. Идентификация нужна для исключения злоупотреблений и позволяет разделить количество запросов от разных пользователей. Ограничения на бесплатные запросы и тарификация описана здесь.

Для идентификации используется API_KEY из_профиля. API_KEY может быть передан как GET так и POST запросом.
Не размещайте ключ API_KEY в открытом доступе и в javascript. Он фактически заменяет ваши логин пароль и дает возможность использовать весь функционал нашего сайта от вашего аккаунта, включая платные функции.

Ключи выбора формата:

  1. html - HTML-формат.
  2. json - JSON-формат
  3. xml - XML-формат

Для указания кодирования ответа в нужном вам формате добавьте в запрос параметр html или json или xml, например:

http://htmlweb.ru/geo/api.php?city=1711&html&charset=utf-8&api_key=xxxx

При возврате в формате JSON возврашается заголовок header('Access-Control-Allow-Origin: *') - разрешающий кроссдоменные запросы.

Ключ выбора кодировки charset=:

  1. windows-1251 по умолчанию всё отдается в кодировке utf-8
  2. utf-8 unicode - кодировка UTF-8. Пример: charset=utf-8
  3. а также другие: koi-8, ISO-8859-1, ISO-8859-15, cp866, cp1252, KOI8-R

fields - какие поля включать в ответ, например:
http://htmlweb.ru/geo/api.php?city=1711&sql=pb_city&fields=id,name,english,area,rajon,country

Если Вам нужен другой формат или другая кодировка, а также обо всех найденных проблемах и пожеланиях сообщайте нам.


Структура API запроса для получения списка городов соответствующих шаблону:

http://htmlweb.ru/geo/api.php?html&city_name=НАЧАЛО_НАЗВАНИЯ_ГОРОДА&api_key=API_KEY_из_профиля

Структура API запроса для получения списка городов в json-формате, соответствующих шаблону:

http://htmlweb.ru/geo/api.php?json&city_name=НАЧАЛО_НАЗВАНИЯ_ГОРОДА&api_key=API_KEY_из_профиля

Структура API запроса для получения списка городов в option/select-формате для отображения выпадающего списка:

http://htmlweb.ru/geo/api.php?city_name=НАЧАЛО_НАЗВАНИЯ_ГОРОДА&api_key=API_KEY_из_профиля

Пример для города Москва с Вашим API_KEY:

http://htmlweb.ru/geo/api.php?html&city_name=москва&api_key=НЕДОСТУПНО_БЕЗ_РЕГИСТРАЦИИ

Ответ для html:

<a href="/geo/country/RU/city/1"><b>Москва (Московская область,Домодедовский район)</b></a><br>

Ответ для option/select:

<option value='1'>Москва (Московская область,Домодедовский район)</option>

Ответ для json:

{"0": {
    "id": 1,
    "name": "Москва",
    "area": 1,
    "telcod": 495.499,
    "latitude": 55.7558,
    "longitude": 37.6176,
    "time_zone": 3,
    "tz": "",
    "english": "Moscow",
    "rajon": 3597,
    "sub_rajon": 0,
    "country": "RU",
    "sound": "M210",
    "level": 1,
    "iso": "MOW",
    "vid": 1,
    "post": 101000,
    "geonameid": null,
    "wiki": "ru.wikipedia.org/wiki/%D0%92%D0%BD%D1%83%D0%BA%D0%BE%D0%B2%D0%BE_(%D1%80%D0%B0%D0%B9%D0%BE%D0%BD_%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D1%8B)",
    "full_english": "Moscow (Moscow oblast, Domodedovskij rajon)",
    "full_name": "Москва (Московская область, Домодедовский район)"
  }
}

Параметр limit в ответе показывает количество оставшихся запросов до конца суток для бесплатных запросов или количество запросов до окончания тарифа при платных. Ограничения на бесплатные запросы и тарификация описана здесь.

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

Добавьте сайт, на котором Вы собираетесь разместить скрипт в Профиль->ограничить домены. В код страницы вставьте следующий HTML-код:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="Author" content="Kolesnikov D.G." />
<title>Выбор города из выпадающего списка по начальным буквам</title>
    <script type="text/javascript" src="/geo/api.js" async></script>
    <script type="text/javascript" src="/js.js" async></script>
</head>

<body style="max-width: 600px">
<h1>Выбор города из выпадающего списка по начальным буквам</h1>

<form method="get" action="/geo/city.php"
      onsubmit="return CityChoice();">
<p style='font-size:15px;text-align: left'><b>Введите город:</b>
<input style='font-size:15px;width:450px' id="city" size="60"
       placeholder="Начните вводить название"
       onfocus="_geo.o_info=getObj('info');_geo.f_Choice=CityChoice;_geo.init(this);" />
</p>
</form>
<br>

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

<script type="text/javascript"><!--
function CityChoice(e){
//ajaxLoad(_geo.o_info, '/geo/api.php?city='+_geo.city_id);
updateObj(_geo.o_info, 'Выбран <b>'+_geo.city_name+'</b>, код: <b>'+_geo.city_id+'</b>');
return false;
}
//--></script>
</body>
</html>

Код скрипта api.js:

//"use strict"
var _geo={/* скрипт скачан с htmlweb.ru/geo/ */
    city_name:"",
    city_id:0,
    timer:0,
    x:-1,
    y:0,
    o_city:null,   /* input, по умолчанию id='city' */
    o_select:null,
    o_info:null, /* div для вывода результата информации о городе, по умолчанию id='info'*/
    f_Choice:null, /* функция, которая вызывается после выбора, по умолчанию 'CityChoce'*/
    json:false,

    //g=getObj('city').onkeyup=PressKey; // альтернативный вариант назначения обработчика

    init:function(t){ /* t='id input-а' */
        if(!_geo.o_info)_geo.o_info=getObj('info');
        var e;
        if(typeof(t)=='object')e=_geo.o_city=t;
        else e=_geo.o_city=getObj((typeof(t)=='string'?t:'city'));
        //console.log(e.id);
        addEvent(e, 'keyup',_geo.PressKey);
        e.setAttribute('autocomplete','off');
        //t=t+'_select';
        if(!_geo.o_select){
            /* создаем селект
            <select id="info_sel" size=5 style='visibility:hidden;position:absolute;z-index:999;'
                onChange="getObj('city').value=city_name=this.options[this.selectedIndex].text;city_id=this.options[this.selectedIndex].value;"
                onkeyup='PressKey2(event)' ondblclick='return CityChoce()'>
            </select>*/
            _geo.o_select=e=document.createElement('select');
            //e.setAttribute('id',t);
            e.setAttribute('size',5);
            e.setAttribute('style','visibility:hidden;position:absolute;z-index:999;');
            addEvent(e, 'change',function(){_geo.o_city.value=_geo.city_name=this.options[this.selectedIndex].text;_geo.city_id=this.options[this.selectedIndex].value;});
            addEvent(e, 'keyup',_geo.PressKey2);
            addEvent(_geo.o_select, 'dblclick', _geo.Choice);
            document.body.appendChild(e);
        }

        //console.log(_geo.f_Choce);

        _geo.x=-1; _geo.y=0; _geo.city_name="";

    },

    Choice:function(e){
        if(!_geo.f_Choice)_geo.f_Choice=CityChoice;
        _geo.f_Choice(e);
        _geo.o_city.focus();
        _geo.o_city.select();
        _geo.o_select.style.visibility = 'hidden'; // спрячем select
    },

    PressKey2:function(e){ // вызывается при нажатии клавиши в select
        e=e||window.event;
        t=(window.event) ? window.event.srcElement : e.currentTarget; // объект для которого вызвано
        if(e.keyCode==13){ // Enter
           //_geo.o_city.form.onsubmit();
           _geo.o_city.value=_geo.city_name=t.options[t.selectedIndex].text;_geo.city_id=t.options[t.selectedIndex].value;
           _geo.Choice();
           return;}
        if(e.keyCode==38&&t.selectedIndex==0){ // Up
           _geo.o_city.focus();
           _geo.o_select.style.visibility = 'hidden'; // спрячем select
        }
    },

    // Определение координаты элемента
    pageX:function(elem) {
        return elem.offsetParent ?
            elem.offsetLeft + _geo.pageX( elem.offsetParent ) :
            elem.offsetLeft;
    },

    pageY:function(elem) {
        return elem.offsetParent ?
            elem.offsetTop + _geo.pageY( elem.offsetParent ) :
            elem.offsetTop;
    },

    PressKey:function(e){ /* вызывается при отпускании кнопки в input */
        e=e||window.event;
        t=(window.event) ? window.event.srcElement : e.currentTarget; // объект для которого вызвано
        g=_geo.o_select;
        if(_geo.x==-1&&_geo.y==0){// при первом обращении просчитываю координаты
            _geo.x=_geo.pageX(t); _geo.y=_geo.pageY(t);
            g.style.top = _geo.y + t.clientHeight+1 + "px";
            g.style.left = _geo.x + "px";
        }
        if(e.keyCode==40){g.focus();g.selectedIndex=0;return;}
        if(_geo.city_name==t.value)return; // если ничего не изменилось не "замучить" сервер
        _geo.city_name=t.value;
        if(_geo.timer){clearTimeout(_geo.timer);_geo.timer=0;}
        if(_geo.city_name.length<3){
            g.style.visibility = 'hidden'; // спрячем select
            return;}
        _geo.timer=window.setTimeout('_geo.Load()',1000);  // загружаю через 1 секунду после последнего нажатия клавиши
    },

    Load:function(){
       _geo.timer=0;
       _geo.o_select.options.length=0;
       ajaxLoad(_geo.o_select, '/geo/api.php?city_name='+_geo.city_name);
       _geo.o_select.style.visibility='visible';
    },


    getGeo:function(p,f){ /* p=0 - по IP, p=1 по данным браузера с запросом пользователя, p=2 - точно по данным браузера с запросом пользователя и использованием GPS
        f=html или f=json */
        if(!p || !navigator.geolocation){

            ajaxLoad(_geo.o_info, '/geo/api.php?ip'+(f?'&'+f:''),'Загрузка...','',
                function (obj, ajaxObj, callback){
                    if(ajaxObj.readyState==4) updateObj(obj, ajaxObj.responseText);
                    hide('wait');
                    return false;
                });

        }else
        if(navigator.geolocation) {
            _geo.timer = setTimeout("_geo.geolocFail()", 10000);

            navigator.geolocation.getCurrentPosition(function(position) {
                clearTimeout(_geo.timer); _geo.timer=0;
                //updateObj(_geo.o_info, "latitude (широта): <b>" + position.coords.latitude + "</b> , longitude (долгота): <b>" + position.coords.longitude + "</b>");
            ajaxLoad(_geo.o_info, '/api/geo/city_coming?latitude='+position.coords.latitude+'&longitude='+position.coords.longitude+'&perpage=1'+(f?'&'+f:''),'Загрузка...','',
                function (obj, ajaxObj, callback){
                    if(ajaxObj.readyState==4) updateObj(obj, ajaxObj.responseText);
                    hide('wait');
                    return false;
                }
            );

            }, function(error) {
                clearTimeout(_geo.timer); _geo.timer=0;
                _geo.geolocFail(error);
            },
            {
                maximumAge:100000,
                timeout:10000,
                enableHighAccuracy:!!(p==2)
            });
        } else {
            _geo.geolocFail();
        }
    },

    geolocFail:function(e){
        if(typeof(e)=='object'){
          switch (e.code) {
            case e.PERMISSION_DENIED:
              updateObj(_geo.o_info, "Посетитель не дал доступ к сведениям о местоположении", true);
              break;
            case e.POSITION_UNAVAILABLE:
              updateObj(_geo.o_info, "Невозможно получить сведения о местоположении", true);
              break;
            case e.TIMEOUT:
              updateObj(_geo.o_info, "Истёк таймаут, в течение которого должны быть получены данные о местоположении", true);
              break;
            default:
              updateObj(_geo.o_info, "Возникла ошибка '" + e.message + "' с кодом " + e.code, true);
          }
        }else{
            updateObj(_geo.o_info, "Ваш браузер не поддерживает гео-локацию", true);
        }
    }

};

/* дальше идут общие функции */
if(0){
    function getObj(objID){
        if (document.getElementById) {return document.getElementById(objID);}
        else if (document.all) {return document.all[objID];}
        else if (document.layers) {return document.layers[objID];}
        return'';
    }

    function ajaxLoad(obj,url,defMessage,post,callback){
        var ajaxObj;
        if(typeof(obj)!="object")obj=document.getElementById(obj);
        if(defMessage&&obj)obj.innerHTML=defMessage;
        if(window.XMLHttpRequest){
            ajaxObj = new XMLHttpRequest();
        } else if(window.ActiveXObject){
            ajaxObj = new ActiveXObject("Microsoft.XMLHTTP");
        } else {
            return false;
        }
        ajaxObj.open ((post?'POST':'GET'), url);
        if(post&&ajaxObj.setRequestHeader){
            if(post=='chat'){ajaxObj.chat=true;post='';}
            else ajaxObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8;");
        }
        ajaxObj.setRequestHeader("Referer", window.location.href);
        ajaxObj.onreadystatechange = ajaxCallBack(obj,ajaxObj,(callback?callback:null));
        ajaxObj.send(post);
        return false;
    }

    if (!window.getComputedStyle) { // борьба с IE
        window.getComputedStyle = function(el, pseudo) {
            this.el = el;
            this.getPropertyValue = function (prop) {
                var re = /(\-([a-z]){1})/g;
                if (prop == "float") prop = "styleFloat";
                if (re.test(prop)) {
                    prop = prop.replace(re, function () {
                        return arguments[2].toUpperCase();
                    });
                }
                return el.currentStyle[prop] ? el.currentStyle[prop] : null;
            };
            return this;
        }
    }

    function updateObj(obj, data, bold, blink){
        if(bold)data=data.bold();
        if(blink)data=data.blink();
        if(typeof(obj)!="object")obj=document.getElementById(obj);
        o=obj;
        do{    if(o.style){c=window.getComputedStyle(o, null);
            if(c.display!="block"&&c.display!="inline"&&c.display.substr(0,5)!="table"){o.style.display=(o.tagName=="DIV"?"block":"inline");}
        }
            o=o.parentNode;
        }while(o);
        ajaxEval(obj, data);
    }

    function ajaxEval(obj, data){
        if(obj.tagName=='INPUT'||obj.tagName=='TEXTAREA'){
            if(obj.value!=data){
                obj.value=data;
                if(obj.onchange!=null)obj.onchange(obj);
            }
        }else if(obj.tagName=='SELECT'){
            if(typeof(data)=='number' || data.indexOf('<')<0){ // это value
                for(i=0;i<obj.options.length;i++)
                    if(obj.options[i].value==data){obj.options[i].selected=true;break;}
            }else{
                obj.options.length = 0;
                var re=new RegExp ("<option[^<]+</option>","img");
                data=data.match(re);
                if(data){
                    for(i=0;i<data.length;i++){
                        var re0 = new RegExp ("value=[\'\"]([^\'\"]+)[\'\"]?","i"); value=re0.exec(data[i]); value= value==null? '' : value[1];
                        if(!value){var re0 = new RegExp ("value=([^<>]+)","i"); value=re0.exec(data[i]); value= value==null? '' : value[1];}
                        var re1=new RegExp ("<option[^>]+>([^<]+)</option>","i"); text=re1.exec(data[i]); text= text==null? null : text[1];
                        var re4 = new RegExp ("class=[\'\"]([^\'\"]+)[\'\"]","i"); defclass=re4.exec(data[i]);
                        j=obj.options.length;
                        if (text !=null){
                            var re2 = /selected/i; defSelected=re2.test(data[i]);
                            obj.options[j] = new Option(text, value,defSelected,defSelected);
                            var re3 = /disabled/i; if(re3.test(data[i]))obj.options[j].disabled=true;
                            if(defclass!=null) obj.options[j].className=defclass[1];
                        }else obj.options[j] = new Option('ОШИБКА!', '' );
                    }
                }}
        }else if(typeof(data)=='object' && obj.tagName=='A'){
            //console.log('data=',data, ', obj=',obj);
            for(k in data){
                //console.log(obj,'[',k, '] =',data[k]);
                if(k=='innerHTML')obj.innerHTML=data[k];
                else obj.setAttribute(k, data[k]);
            }
        }else obj.innerHTML = data;
    }

    function ajaxJson(obj, data){
        if(!data)return;
        ajaxObj=eval("(" + data + ")");
        if(obj.tagName!="FORM"&&obj.form)obj=obj.form;
        if(obj.tagName!="FORM"){
            ajaxEval(obj, ajaxObj);
            return;
        }
        for(key in ajaxObj){
            o=obj[key];
            if(typeof(ajaxObj[key])=='object'){
                if(typeof(o)=='object' && o.tagName=='SELECT'){
                    o.options.length = 0; j=0;
                    s=ajaxObj[key];
                    for(k in s){
                        m=s[k];
                        if(typeof(m)=='object'){
                            if(typeof(m['selected'])=='undefined')m['selected']=false;
                            if(typeof(m['value'])=='undefined')m['value']=k;
                            o.options[j++] = new Option(m['text'], m['value'] ,m['selected'],m['selected']);
                            for(var k1 in m)if(k1!='text'&&k1!='value'&&k1!='selected')o.options[j-1].setAttribute(k1,m[k1]);
                        }else{
                            o.options[j++] = new Option(m, k,false,false);
                        }
                    }
                }else{
                    s=(typeof(o)=='undefined'?document.createElement("input"):o);
                    s.setAttribute('name', key);
                    s.setAttribute('type', 'hidden');/*по умолчанию скрытый*/
                    if(typeof(o)=='undefined')obj.appendChild(s);
                    o=ajaxObj[key];
                    for(k in o)s.setAttribute(k, o[k]);
                }
            }else if(typeof(o)!='undefined'&&typeof(o)!='null'){
                ajaxEval(o, ajaxObj[key]);
            }else if((pos=key.indexOf('.'))>0){ // имя.атрибут=значение
                o=key.substr(0,pos);
                o=obj[o];
                if(typeof(o)!='undefined'&&typeof(o)!='null'){
                    s=key.substr(pos+1);
                    if(s=='disabled')o.disabled=ajaxObj[key];
                    else if(s=='value'&&o.tagName=='SELECT'){
                        o.options.length = 0;
                        t=ajaxObj[key]; s='';
                        if(o.name.substr(o.name.length-3,3)=='_cs'){
                            s=eval('o.form.'+o.name.substr(0,o.name.length-3));
                            if(s)t=s.value;
                        }
                        o.options[0] = new Option(t, ajaxObj[key],true,true);
                        if(s)if(o1=s.getAttribute('after'))eval(o1);
                    }else o.setAttribute(s, ajaxObj[key]);
                }
            }else if(o=getObj(key))ajaxEval(o,ajaxObj[key]);
        }
        if(obj.style)obj.style.display='block';
    }

    function ajaxCallBack(obj, ajaxObj, callback){
        return function(){
            if(ajaxObj.readyState==4){
                if(callback) if(!callback(obj,ajaxObj))return;
                if (ajaxObj.status==200){
                    //console.log(ajaxObj.responseText);
                    if(ajaxObj.getResponseHeader("Content-Type").indexOf("application/x-javascript")>=0){
                        eval(ajaxObj.responseText.replace(/\n/g,";").replace(/\r/g,""));
                    }else if(ajaxObj.getResponseHeader("Content-Type").indexOf('json')>=0){
                        ajaxJson(obj,ajaxObj.responseText);
                    }else updateObj(obj, ajaxObj.responseText);
                }
                else updateObj(obj, ajaxObj.status+' '+ajaxObj.statusText,1,1);
            }
            else if(ajaxObj.readyState==3&&ajaxObj.chat)obj.innerHTML=ajaxObj.responseText;
        }
    }

    var addEvent = (function(){
        if (document.addEventListener){
            return function(obj, type, fn, useCapture){
                if(!obj)console.error(obj, type, fn, useCapture);
                if(typeof(obj)!="object")obj=document.getElementById(obj);
                //console.log("addEvent:",obj,fn);
                if(obj)obj.addEventListener(type, fn, useCapture);
            }
        } else if (document.attachEvent){ // для Internet Explorer
            return function(obj, type, fn, useCapture){
                if(typeof(obj)!="object")obj=document.getElementById(obj);
                obj.attachEvent("on"+type, fn);
            }
        } else {
            return function(obj, type, fn, useCapture){
                if(typeof(obj)!="object")obj=document.getElementById(obj);
                obj["on"+type] = fn;
            }
        }
    })();

    function removeEvent(obj, eventType, handler)
    {    if(obj&&typeof(obj)!="object")obj=document.getElementById(obj);
        return (obj.detachEvent ? obj.detachEvent("on" + eventType, handler) : ((obj.removeEventListener) ? obj.removeEventListener(eventType, handler, false) : null));
    }

    function getEventTarget(e) {
        e = e || window.event;
        var target=e.target || e.srcElement;
        if(typeof target == "undefined")return e; // передали this, а не event
        if (target.nodeType==3) target=target.parentNode;// боремся с Safari
        return target;
    }


}

Открыть пример

Пример выбора города с использованием универсального скрипта выпадающих подсказок

В данном примере используется скрипт выпадающих подсказок autoComplete. Если Вы используете этот или аналогичный скрипт в других полях, то вам удобнее использовать его и для данного выбора. Так же в нем используется API htmlweb.ru


Код скрипта:

<!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>Выбор города из выпадающего списка по начальным буквам</title>
    <link rel="stylesheet" href="/load/auto-complete.css" type="text/css">
    <script type="text/javascript" src="/load/auto-complete.min.js"></script>
</head>

<body style="max-width: 600px">
<h1>Выбор города из выпадающего списка по начальным буквам</h1>

<form method="get" action="#" onsubmit="return false;">
    <label style='font-size:15px;text-align: left'><b>Введите город:</b>
        <input name="city2" size="60" placeholder="Начните вводить название" value="" autocomplete="off">
    </label>
</form>
<br>

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

<script type="text/javascript">
    new autoComplete({
            selector: 'input[name="city2"]',
            minChars: 2,
            delay:    500,
            source: function(term, response){
                console.log('Ищем ',term);
                fetch('https://htmlweb.ru/json/geo/city_list?format=list&q='+encodeURIComponent(term)
                    /* +'&api_key= взять здесь: https://htmlweb.ru/user/#allowDomain' */)
                    .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);
                            if(data.limit)console.log('Осталось '+data.limit+' запросов');
                            if(data.error)document.getElementById('info').innerHTML=data.error;
                            else if(data.message)document.getElementById('info').innerHTML=data.message;
                            if(data.items) response(data.items);
                        })
                    .catch(
                        function(error) {
                            console.error(error)
                        });
            },
            onSelect: function(event, term, item){
                    document.getElementById('info').innerHTML='Выбран: '+term;
            }
        }
    );
</script>
<br><br><p>Скрипт скачан здесь: <a href="https://htmlweb.ru/geo/city_api_example.php#autoComplete">https://htmlweb.ru/geo/city_api_example.php</a>
</body>
</html>

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

Параметры API метода получения списка городов по шаблону

Все актуальные параметры доступны по ссылке: https://htmlweb.ru/json/geo/city_list?help

Входные параметры:
  • country - страна. Может быть задана кодом или названием
  • q - шаблон имени населенного пункта
  • обязательно задание как минимум одного из q и country
    Необязательные:
  • area - регион, если передан, то выводятся только населенные пункты этого региона
  • rajon - район, если передан, то выводятся только населенные пункты этого района
  • level- размер населенного пункта (0-4)
  • p, perpage - с какой страницы и сколько на странице

  • .