Делаем форму для ввода адреса

в 22:19, , рубрики: api, javascript, автодополнение адреса, Веб-разработка, кладр api, метки: , ,

Сегодня, в день релиза нового jQuery плагина для сервиса КЛАДР в облаке, я хочу показать как можно быстро сделать форму для ввода адреса с автодополнением и валидацией.

Делаем форму для ввода адреса

Для нетерпеливых: исходный код формы можно посмотреть на гитхабе. Для запуска примера вам достаточно скачать его в zip и открыть страницу с примером в браузере.

Несколько слов о плагине и реализованных фичах:

  • Мы избавились от использования jquery.ui.autocomplete. Теперь плагин суммарно весит 13 Кбайт и не требует никаких дополнительных библиотек, кроме непосредственно jQuery.
  • Плагин теперь состоит из 2 отдельных частей: библиотеки для выполнения запросов к сервису (гитхаб) и собственно самого плагина для автодополнения (гитхаб). Это даёт возможность в случае необходимости использовать их отдельно.
  • В плагине для автодополнения мы постарались реализовать весь необходимый функционал: проверку корректности введенного адреса, ajax-крутилку.

Теперь после небольшого резюме нововведений предлагаю вернуться к форме. Начнём с подключения необходимых библиотек.

<link href="../jquery.kladr.min.css" rel="stylesheet">
<link href="css/example5.css" rel="stylesheet">

<script src="js/lib/jquery-1.10.2.min.js" type="text/javascript"></script>
<script src="../jquery.kladr.min.js" type="text/javascript"></script>
<script src="js/example5.js" type="text/javascript"></script>

Так как целью данного поста является не стилевое оформление форм сразу приведу код html и css

HTML

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
        <title>Форма для ввода адреса</title>

        <link href="../jquery.kladr.min.css" rel="stylesheet">
        <link href="css/example5.css" rel="stylesheet">

        <script src="js/lib/jquery-1.10.2.min.js" type="text/javascript"></script>
        <script src="../jquery.kladr.min.js" type="text/javascript"></script>
        <script src="js/example5.js" type="text/javascript"></script>
    </head>
    <body>
        <form>
            <div class="field">
                <label>Регион</label>
                <input type="text" name="region">
            </div>
            <div class="field">
                <label>Район</label>
                <input type="text" name="district">
            </div>
            <div class="field">
                <label>Город</label>
                <input type="text" name="city">
            </div>
            <div class="field">
                <label>Улица</label>
                <input type="text" name="street">
            </div>
            <div class="field">
                <label>Дом</label>
                <input type="text" name="building">
            </div>
            <div class="tooltip" style="display: none;"><b></b><span></span></div>
        </form>
    </body>
</html>

CSS
body, input {
    color: #555;
    font-size: 13px;
    font-family: Helvetica, Arial, sans-serif;
}
form {
    width: 300px;
    margin: 40px auto 0;
    padding: 20px 20px 10px;
    border-radius: 5px;
    border: 1px solid #e1e1e8;
    background-color: #f7f7f9;
    box-shadow: rgba(0,0,0,0.075) 2px 3px 7px;
}
input, button {
    outline: none;
}
.field label {
    display: inline-block;
    width: 80px;
    vertical-align: middle;
}
.field {
    margin-bottom: 10px;
    padding: 0;
}
.field input {
    height: 2em;
    min-width: 196px;
    border-radius: 3px;
    border: 1px solid #d3d3d3;
    box-shadow: inset 0 1px 1px rgba(0,0,0,0.1);
    padding: 0 7px;
    color: #666;
}
.tooltip {
    position: absolute;
    top: 16px;
    left: 360px;
    width: 220px;
    color: #b94a48;
    padding: 8px 10px;
    border-radius: 5px;
    border: 1px solid #eed3d7;
    background-color: #f2dede;
    opacity: 0.8;
}
.tooltip b {
    position: absolute;
    display: block;
    left: -14px;
    width: 0;
    height: 0;
    color: transparent;
    border: 7px solid;
    border-right-color: #f2dede;
}
#kladr_autocomplete ul {
    border-radius: 4px;
    border-color: #d3d3d3;
    padding: 0;
    background-color: #fff;
}
#kladr_autocomplete li {
    padding: 6px 8px;
    border: none;
    border-bottom: 1px solid #ededed;
    background-color: transparent;
}
#kladr_autocomplete ul .active {
    border: none;
    border-bottom: 1px solid  #f0f0f0;
    background-color: #f0f0f0;
    margin-top: -1px;
    padding-top: 7px;
}
#kladr_autocomplete ul li:first-child.active {
    padding-top: 6px;
    margin-top: 0;
}
#kladr_autocomplete li strong {
    color: #5499BB;
}
#kladr_autocomplete .spinner {
    background-image: url("../img/spinner.png");
    width: 16px;
    height: 16px;
}

В итоге у нас получается вот такая вот форма.

Делаем форму для ввода адреса

Осталось сделать автодополнение с валидацией.
Открываем example5.js.
Сохраняем токен, ключ и объекты, которыми будем манипулировать в виде переменных.

$(function() {
    var token = '51dfe5d42fb2b43e3300006e';
    var key   = '86a2c2a06f1b2451a87d05512cc2c3edfdf41969';

    var region   = $('[name="region"]');
    var district = $('[name="district"]');
    var city     = $('[name="city"]');
    var street   = $('[name="street"]');
    var building = $('[name="building"]');
});

Теперь чтобы подключить автодополнение из сервиса, нам в простейшем варианте нужно написать следующее.

region.kladr({
    token: token,
    key: key,
    type: $.kladr.type.region
});

district.kladr({
    token: token,
    key: key,
    type: $.kladr.type.district
});

city.kladr({
    token: token,
    key: key,
    type: $.kladr.type.city
});

street.kladr({
    token: token,
    key: key,
    type: $.kladr.type.street
});

building.kladr({
    token: token,
    key: key,
    type: $.kladr.type.building
});

Для того чтобы автодополнение районов выполнялось из выбранного региона и т.д. задаём родительский объект для нижестоящих полей при выборе элемента в списке.

region.kladr({
    select: function(obj) {
        region.parent().find('label').text(obj.type);
        district.kladr('parentType', $.kladr.type.region);
        district.kladr('parentId', obj.id);
        city.kladr('parentType', $.kladr.type.region);
        city.kladr('parentId', obj.id);
    }
});

district.kladr({
    select: function(obj) {
        district.parent().find('label').text(obj.type);
        city.kladr('parentType', $.kladr.type.district);
        city.kladr('parentId', obj.id);
    }
});

city.kladr({
    select: function(obj) {
        city.parent().find('label').text(obj.type);
        street.kladr('parentType', $.kladr.type.city);
        street.kladr('parentId', obj.id);
        building.kladr('parentType', $.kladr.type.city);
        building.kladr('parentId', obj.id);
    }
});

street.kladr({
    select: function(obj) {
        street.parent().find('label').text(obj.type);
        building.kladr('parentType', $.kladr.type.street);
        building.kladr('parentId', obj.id);
    }
});

building.kladr({
    select: function(obj) {
        building.parent().find('label').text(obj.type);
    }
});

Сделаем проверку введенных данных (на случай если пользователь не будет выбирать название в списке, а введёт его вручную).

var tooltip  = $('.tooltip');

var ShowError = function(input, message){
    tooltip.find('span').text(message);

    var inputOffset = input.offset();
    var inputWidth  = input.outerWidth();
    var inputHeight = input.outerHeight();

    var tooltipHeight = tooltip.outerHeight();

    tooltip.css({
        left: (inputOffset.left + inputWidth + 10) + 'px',
        top: (inputOffset.top + (inputHeight - tooltipHeight)/2 - 1) + 'px'
    });

    tooltip.show();
};

region.kladr({
    verify: true,
    check: function(obj) {
        if(obj){
            region.parent().find('label').text(obj.type);
            district.kladr('parentType', $.kladr.type.region);
            district.kladr('parentId', obj.id);
            city.kladr('parentType', $.kladr.type.region);
            city.kladr('parentId', obj.id);
        } else {
            ShowError(region, 'Неверно введено название региона');
        }
    }
});

district.kladr({
    verify: true,
    check: function(obj) {
        if(obj){
            district.parent().find('label').text(obj.type);
            city.kladr('parentType', $.kladr.type.district);
            city.kladr('parentId', obj.id);
        } else {
            ShowError(district, 'Неверно введено название района');
        }
    }
});

city.kladr({
    verify: true,
    check: function(obj) {
        if(obj){
            city.parent().find('label').text(obj.type);
            street.kladr('parentType', $.kladr.type.city);
            street.kladr('parentId', obj.id);
            building.kladr('parentType', $.kladr.type.city);
            building.kladr('parentId', obj.id);
        } else {
            ShowError(city, 'Неверно введено название населённого пункта');
        }
    }
});

street.kladr({
    verify: true,
    check: function(obj) {
        if(obj){
            street.parent().find('label').text(obj.type);
            building.kladr('parentType', $.kladr.type.street);
            building.kladr('parentId', obj.id);
        } else {
            ShowError(street, 'Неверно введено название улицы');
        }
    }
});

Ну и последний штрих: сделаем чтобы пункты в списке автодополнения регионов и районов форматировались в стиле «Московская обл.»

var LabelFormat = function( obj, query ){
    var label = '';

    var name = obj.name.toLowerCase();
    query = query.toLowerCase();

    var start = name.indexOf(query);
    start = start > 0 ? start : 0;

    if(query.length < obj.name.length){
        label += obj.name.substr(0, start);
        label += '<strong>' + obj.name.substr(start, query.length) + '</strong>';
        label += obj.name.substr(start+query.length, obj.name.length-query.length-start);
    } else {
        label += '<strong>' + obj.name + '</strong>';
    }

    if(obj.typeShort){
        label += ' ' + obj.typeShort + '.';
    }

    return label;
};

region.kladr({
    labelFormat: LabelFormat,
});

district.kladr({
    labelFormat: LabelFormat,
});

Вот и всё =)
Буду рад вашим вопросам и комментариям

Весь JS код

$(function() {
    var token = '51dfe5d42fb2b43e3300006e';
    var key   = '86a2c2a06f1b2451a87d05512cc2c3edfdf41969';

    var region   = $('[name="region"]');
    var district = $('[name="district"]');
    var city     = $('[name="city"]');
    var street   = $('[name="street"]');
    var building = $('[name="building"]');

    var tooltip  = $('.tooltip');

    var LabelFormat = function( obj, query ){
        var label = '';

        var name = obj.name.toLowerCase();
        query = query.toLowerCase();

        var start = name.indexOf(query);
        start = start > 0 ? start : 0;

        if(query.length < obj.name.length){
            label += obj.name.substr(0, start);
            label += '<strong>' + obj.name.substr(start, query.length) + '</strong>';
            label += obj.name.substr(start+query.length, obj.name.length-query.length-start);
        } else {
            label += '<strong>' + obj.name + '</strong>';
        }

        if(obj.typeShort){
            label += ' ' + obj.typeShort + '.';
        }

        return label;
    };

    var ShowError = function(input, message){
        tooltip.find('span').text(message);

        var inputOffset = input.offset();
        var inputWidth  = input.outerWidth();
        var inputHeight = input.outerHeight();

        var tooltipHeight = tooltip.outerHeight();

        tooltip.css({
            left: (inputOffset.left + inputWidth + 10) + 'px',
            top: (inputOffset.top + (inputHeight - tooltipHeight)/2 - 1) + 'px'
        });

        tooltip.show();
    };

    region.kladr({
        token: token,
        key: key,
        type: $.kladr.type.region,
        labelFormat: LabelFormat,
        verify: true,
        select: function(obj) {
            region.parent().find('label').text(obj.type);
            district.kladr('parentType', $.kladr.type.region);
            district.kladr('parentId', obj.id);
            city.kladr('parentType', $.kladr.type.region);
            city.kladr('parentId', obj.id);
        },
        check: function(obj) {
            if(obj){
                region.parent().find('label').text(obj.type);
                district.kladr('parentType', $.kladr.type.region);
                district.kladr('parentId', obj.id);
                city.kladr('parentType', $.kladr.type.region);
                city.kladr('parentId', obj.id);
            } else {
                ShowError(region, 'Неверно введено название региона');
            }
        }
    });

    district.kladr({
        token: token,
        key: key,
        type: $.kladr.type.district,
        labelFormat: LabelFormat,
        verify: true,
        select: function(obj) {
            district.parent().find('label').text(obj.type);
            city.kladr('parentType', $.kladr.type.district);
            city.kladr('parentId', obj.id);
        },
        check: function(obj) {
            if(obj){
                district.parent().find('label').text(obj.type);
                city.kladr('parentType', $.kladr.type.district);
                city.kladr('parentId', obj.id);
            } else {
                ShowError(district, 'Неверно введено название района');
            }
        }
    });

    city.kladr({
        token: token,
        key: key,
        type: $.kladr.type.city,
        verify: true,
        select: function(obj) {
            city.parent().find('label').text(obj.type);
            street.kladr('parentType', $.kladr.type.city);
            street.kladr('parentId', obj.id);
            building.kladr('parentType', $.kladr.type.city);
            building.kladr('parentId', obj.id);
        },
        check: function(obj) {
            if(obj){
                city.parent().find('label').text(obj.type);
                street.kladr('parentType', $.kladr.type.city);
                street.kladr('parentId', obj.id);
                building.kladr('parentType', $.kladr.type.city);
                building.kladr('parentId', obj.id);
            } else {
                ShowError(city, 'Неверно введено название населённого пункта');
            }
        }
    });

    street.kladr({
        token: token,
        key: key,
        type: $.kladr.type.street,
        select: function(obj) {
            street.parent().find('label').text(obj.type);
            building.kladr('parentType', $.kladr.type.street);
            building.kladr('parentId', obj.id);
        },
        check: function(obj) {
            if(obj){
                street.parent().find('label').text(obj.type);
                building.kladr('parentType', $.kladr.type.street);
                building.kladr('parentId', obj.id);
            } else {
                ShowError(street, 'Неверно введено название улицы');
            }
        }
    });

    building.kladr({
        token: token,
        key: key,
        type: $.kladr.type.building,
        select: function(obj) {
            building.parent().find('label').text(obj.type);
        }
    });
});

Делаем форму для ввода адреса

Автор: xescoder

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js