Подсказки при наборе адреса на сайт


Наверняка вы видели на некоторых сайтах – подсказки. Очень много где требуется регистрироваться или прост вводить данные (например, адрес), то как только вы начинаете нажимать на клавиши, то снизу появляются варианты выбора. Например, вводим «Вол» и внизу появляется – «Вологда», «Волгоград». Вы кликаете на город и он сразу же ставится в поле ввода. Давайте разберемся как это сделать просто и бесплатно.

Схема работы очевидна – ловим событие в текстовом поле (нажатие или изменение), отправляем по ajax, осуществляем поиск и внизу выводим. Самое сложное тут это – поиск. Точнее, что и где искать.

Первое, что приходит в голову – это создание своей базы данных. То есть, если мы говорим об адресе, то список регионов, городов и сел. Взять готовые или спарсить откуда-то – в принципе это возможно. Но как быть с улицами? Номерами домов? И самый главный момент – актуальность информации.

Приходим к выводу, что лучше использовать внешние сервисы. Я обычно использую для этого дадату. Регистрируетесь и получаете 500 бесплатных запросов адреса в день. Для небольшого начинающего проекта этого хватит за глаза. А если разрастётесь – тогда можно и прикупить, не проблема.

Теперь соберем все из кубиков

Фронт (верстка)

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Поиск адреса</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
        }

        .search-container {
            position: relative;
            max-width: 500px;
            margin: 0 auto;
        }

        #address-input {
            width: 100%;
            padding: 12px;
            font-size: 16px;
            border: 1px solid #ccc;
            border-radius: 6px;
            box-sizing: border-box;
        }

        .suggestions {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            background: white;
            border: 1px solid #ddd;
            border-top: none;
            border-radius: 0 0 6px 6px;
            max-height: 200px;
            overflow-y: auto;
            z-index: 1000;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }

        .suggestion-item {
            padding: 10px 12px;
            cursor: pointer;
            border-bottom: 1px solid #eee;
        }

        .suggestion-item:hover {
            background-color: #f5f5f5;
        }

        .error {
            color: red;
            font-size: 14px;
            margin-top: 5px;
        }
    </style>
</head>
<body>
    <div class="search-container">
        <input type="text" id="address-input" placeholder="Введите адрес..." />
        <div id="suggestions-list" class="suggestions"></div>
        <div id="error-message" class="error"></div>
    </div>
</body>
</html>
JavaScript

    <script>
        const input = document.getElementById('address-input');
        const suggestionsList = document.getElementById('suggestions-list');
        const errorMessage = document.getElementById('error-message');

        let timeoutId = null;

        input.addEventListener('input', function () {
            const query = input.value.trim();

            clearTimeout(timeoutId);

            // Очистка ошибок и подсказок
            errorMessage.textContent = '';
            suggestionsList.innerHTML = '';

            if (query.length < 3) return;

            timeoutId = setTimeout(() => {
                fetch('api.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ query })
                })
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`Ошибка сети: ${response.status}`);
                    }
                    return response.json();
                })
                .then(data => {
                    if (data.error) {
                        errorMessage.textContent = data.error;
                        return;
                    }

                    if (!data.suggestions || data.suggestions.length === 0) {
                        suggestionsList.innerHTML = '<div class="suggestion-item">Ничего не найдено</div>';
                        return;
                    }

                    suggestionsList.innerHTML = data.suggestions
                        .map(item => {
                            return `<div class="suggestion-item" data-value="${item.unrestricted_value}">
                                        ${item.value}
                                    </div>`;
                        })
                        .join('');

                    // Добавляем обработчики клика
                    document.querySelectorAll('.suggestion-item').forEach(item => {
                        item.addEventListener('click', function () {
                            input.value = this.dataset.value;
                            suggestionsList.innerHTML = '';
                        });
                    });
                })
                .catch(err => {
                    errorMessage.textContent = 'Ошибка: ' + err.message;
                });
            }, 300); // Дебаунс
        });

        // Скрываем подсказки при клике вне поля
        document.addEventListener('click', function (e) {
            if (!input.contains(e.target) && !suggestionsList.contains(e.target)) {
                suggestionsList.innerHTML = '';
            }
        });
    </script>
Сервер (PHP)

<?php
// api.php

header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); // Для разработки. На продакшене укажите ваш домен
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');

$token = "ваш_токен_от_dadata"; // ← Замените на реальный токен

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Метод не поддерживается'], JSON_UNESCAPED_UNICODE);
    exit;
}

$input = json_decode(file_get_contents('php://input'), true);

if (!isset($input['query']) || empty(trim($input['query']))) {
    echo json_encode(['suggestions' => []]);
    exit;
}

$query = trim($input['query']);

// Параметры запроса к DaData
$url = "https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address";
$headers = [
    'Content-Type: application/json',
    'Accept: application/json',
    "Authorization: Token {$token}",
];

$postData = [
    'query' => $query,
    'count' => 5, // Ограничим количество подсказок
];

$ch = curl_init();

curl_setopt_array($ch, [
    CURLOPT_URL            => $url,
    CURLOPT_HTTPHEADER     => $headers,
    CURLOPT_TIMEOUT        => 10,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($postData, JSON_UNESCAPED_UNICODE),
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_FOLLOWLOCATION => false,
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);

curl_close($ch);

if ($error) {
    http_response_code(500);
    echo json_encode(['error' => 'Ошибка подключения к DaData', 'details' => $error], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($httpCode !== 200) {
    http_response_code($httpCode);
    echo json_encode(['error' => 'Ошибка от DaData', 'status' => $httpCode], JSON_UNESCAPED_UNICODE);
    exit;
}

$result = json_decode($response, true, 512, JSON_THROW_ON_ERROR);

// Возвращаем только нужные поля
$suggestions = array_map(function ($item) {
    return [
        'value' => $item['value'],
        'unrestricted_value' => $item['unrestricted_value']
    ];
}, $result['suggestions'] ?? []);

echo json_encode(['suggestions' => $suggestions], JSON_UNESCAPED_UNICODE);
На сайте дадаты есть и другие апи – поиск по ИНН, названию компании – все в разумных пределах бесплатно.

Вот так просто можно реализовать подсказки при наборе адреса (живой поиск).
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, php, javascript, программирование, данные



Согласие на обработку персональных данных
Урок 39. Коллекция Стек (Stack) C#
Урок 27. Интерфейс Comparable в Java