SearchBox виджет

SearchBox представляет собой специализированный UI-компонент библиотеки Google Maps JavaScript API, предназначенный для поиска мест и адресов с использованием сервиса Places. Он работает поверх текстового поля ввода и подключается к автодополнению Google Places, возвращая структурированные объекты мест (PlaceResult), которые затем могут быть использованы для центрирования карты, отображения маркеров и получения детальной информации о выбранной точке.

Основная особенность SearchBox заключается в том, что он ориентирован не на одиночное предсказание, а на поиск и выбор конкретных мест с возможностью привязки к географическим границам карты.


Подключение Google Maps JavaScript API и библиотеки Places

Для корректной работы SearchBox требуется загрузка API с подключением библиотеки Places.

<script
  src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"
  defer
></script>

Ключевой момент — параметр libraries=places. Без него класс google.maps.places.SearchBox будет недоступен.


Базовая HTML-разметка

SearchBox обычно привязывается к стандартному элементу <input>:

<input
  id="search-box"
  type="text"
  placeholder="Поиск мест..."
  style="width: 300px; padding: 8px;"
/>

<div id="map" style="height: 500px;"></div>

Элемент ввода используется как источник текста запроса, а карта — как область отображения результатов.


Основная логика инициализации строится вокруг создания карты и подключения SearchBox к input-элементу.

let map;
let searchBox;
let markers = [];

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: 48.0196, lng: 66.9237 },
    zoom: 6,
  });

  const input = document.getElementById("search-box");

  searchBox = new google.maps.places.SearchBox(input);

  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
}

На этом этапе SearchBox уже связан с полем ввода, но ещё не обрабатывает результаты.


Привязка SearchBox к области карты (biasing)

Одной из ключевых возможностей SearchBox является ограничение результатов текущими границами карты. Это повышает релевантность выдачи.

map.addListener("bounds_changed", () => {
  searchBox.setBounds(map.getBounds());
});

Метод setBounds() задаёт область предпочтительного поиска. Это не строгая фильтрация, а смещение приоритета результатов.


Обработка выбора места

SearchBox генерирует событие places_changed, которое срабатывает при выборе одного или нескольких результатов.

searchBox.addListener("places_changed", () => {
  const places = searchBox.getPlaces();

  if (!places || places.length === 0) {
    return;
  }

  markers.forEach(marker => marker.setMap(null));
  markers = [];
});

Метод getPlaces() возвращает массив объектов PlaceResult, каждый из которых содержит:

  • геометрию (geometry)
  • координаты (location)
  • bounding box (viewport)
  • название места (name)
  • типы (types)
  • идентификатор place_id

Отображение маркеров на карте

После получения мест обычно выполняется визуализация результатов:

const bounds = new google.maps.LatLngBounds();

places.forEach(place => {
  if (!place.geometry || !place.geometry.location) return;

  const marker = new google.maps.Marker({
    map,
    title: place.name,
    position: place.geometry.location,
  });

  markers.push(marker);

  if (place.geometry.viewport) {
    bounds.union(place.geometry.viewport);
  } else {
    bounds.extend(place.geometry.location);
  }
});

После обработки результатов карта подстраивается под выбранные координаты:

map.fitBounds(bounds);

Структура объекта PlaceResult

SearchBox возвращает унифицированный объект места. Основные поля:

geometry

Содержит географическую информацию:

  • location — координаты точки
  • viewport — рекомендуемая область отображения

name

Название объекта (например, ресторан, город, организация).

formatted_address

Полный адрес в читаемом виде.

place_id

Уникальный идентификатор места в системе Google Places.

types

Категории объекта:

  • restaurant
  • locality
  • establishment
  • geocode

Настройка типов поиска

SearchBox позволяет ограничить типы возвращаемых объектов через setTypes().

searchBox.setTypes(["geocode"]);

Типы влияют на характер выдачи:

  • geocode — адреса и географические точки
  • address — только адреса
  • (regions) — регионы
  • (cities) — города

Ограничение поиска (strictBounds)

SearchBox поддерживает строгую привязку к области карты:

searchBox.setOptions({
  strictBounds: true,
});

При включении этого режима результаты вне текущих границ карты полностью исключаются, а не просто понижаются в приоритете.


Разница между SearchBox и Autocomplete

Несмотря на схожесть, SearchBox и Autocomplete имеют разные сценарии использования.

  • ориентирован на поиск списка мест
  • возвращает несколько результатов
  • лучше подходит для поиска по карте

Autocomplete:

  • ориентирован на ввод с подсказками
  • возвращает одно активное предсказание
  • чаще используется в формах адресов

SearchBox фактически является надстройкой над Places Autocomplete с поддержкой множественных результатов.


Фильтрация и управление результатами

SearchBox не предоставляет полноценного серверного фильтра, но позволяет:

  • ограничивать область поиска через bounds
  • задавать типы через setTypes
  • использовать strictBounds
  • комбинировать с логикой приложения

Пример комбинированной настройки:

searchBox.setOptions({
  bounds: map.getBounds(),
  strictBounds: true,
  types: ["establishment"],
});

Работа с place_id

Для получения расширенной информации о месте используется place_id.

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

const service = new google.maps.places.PlacesService(map);

service.getDetails(
  {
    placeId: place.place_id,
    fields: ["name", "formatted_address", "rating", "geometry"],
  },
  (result, status) => {
    if (status === google.maps.places.PlacesServiceStatus.OK) {
      console.log(result);
    }
  }
);

Управление памятью и маркерами

При частом использовании SearchBox важно контролировать жизненный цикл маркеров:

  • удаление старых маркеров перед добавлением новых
  • предотвращение накопления DOM-ссылок
  • очистка обработчиков событий при уничтожении карты

Типовая схема очистки:

markers.forEach(marker => marker.setMap(null));
markers = [];

Оптимизация производительности

При интенсивной работе SearchBox имеет смысл учитывать следующие аспекты:

Ограничение количества запросов

SearchBox сам управляет запросами, но чрезмерное перемещение карты может косвенно увеличивать нагрузку.

Использование debounce в UI

Если input дополнительно обрабатывается, применяется задержка ввода.

Минимизация перерисовки карты

Избыточные вызовы fitBounds могут приводить к “дёрганию” интерфейса.


Обработка пустых и ошибочных результатов

SearchBox может вернуть пустой массив или объекты без geometry. Это стандартный сценарий, который необходимо учитывать:

if (!place.geometry || !place.geometry.location) {
  return;
}

Также возможны ситуации, когда пользователь вводит текст, не соответствующий Places API.


Расширенные сценарии использования

SearchBox часто используется не только для поиска, но и как входная точка для гео-логики приложения:

Построение маршрутов

Выбор двух SearchBox (откуда / куда) с последующей передачей координат в DirectionsService.

Фильтрация бизнес-объектов

Ограничение типов establishment для поиска организаций.

Гео-аналитика

Сбор place_id для последующего анализа через Places Details API.


Особенности поведения bounds_changed

Важно учитывать, что событие bounds_changed может вызываться часто при движении карты. Привязка SearchBox к нему должна быть лёгкой операцией:

map.addListener("bounds_changed", () => {
  searchBox.setBounds(map.getBounds());
});

Избыточная логика внутри обработчика может привести к деградации производительности.


Управление input-элементом

SearchBox не создаёт собственный UI. Он полностью зависит от внешнего input:

  • можно стилизовать через CSS
  • можно перемещать в DOM
  • можно подключать к кастомным интерфейсам

Пример кастомного позиционирования:

map.controls[google.maps.ControlPosition.TOP_CENTER].push(input);

Обработка множественных результатов

SearchBox может возвращать несколько мест одновременно, особенно при общих запросах:

  • названия городов
  • сети заведений
  • популярные локации

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

places.forEach(place => {
  // обработка каждого результата
});

Типичные ошибки интеграции

Отсутствие libraries=places

Приводит к undefined для SearchBox.

Использование без map instance

SearchBox может работать без карты, но теряется контекст bounds.

Игнорирование geometry

Некоторые места не имеют координат в кратком ответе.

Отсутствие очистки маркеров

Приводит к накоплению объектов на карте.


Совместимость с современными версиями API

SearchBox поддерживается в Google Maps JavaScript API v3 и остаётся стабильным компонентом. При этом Google постепенно смещает акцент в сторону Place Autocomplete (нового поколения), однако SearchBox продолжает использоваться в сценариях картографического поиска с множественными результатами.