Всплывающие окна в Google Maps JavaScript API реализуются через
несколько уровней абстракции: стандартный InfoWindow,
кастомизированные HTML-вставки внутри него, а также полностью
пользовательские оверлеи на основе OverlayView. Каждый
подход решает разные задачи — от простого отображения текста до сложных
интерактивных интерфейсов, встроенных в карту.
InfoWindow представляет собой стандартный компонент для
отображения контекстной информации над маркером или произвольной точкой
карты. Он привязывается к координатам и автоматически управляет
позиционированием при панорамировании и масштабировании карты.
Создание базового всплывающего окна:
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 52.4, lng: 71.5 },
zoom: 6,
});
const marker = new google.maps.Marker({
position: { lat: 52.4, lng: 71.5 },
map: map,
});
const infoWindow = new google.maps.InfoWindow({
content: "<div><strong>Объект</strong><br>Описание точки</div>",
});
marker.addListener("click", () => {
infoWindow.open({
anchor: marker,
map,
});
});
InfoWindow поддерживает HTML-разметку, но её сложность
ограничена: вложенные интерактивные компоненты, обработчики событий и
динамическое обновление DOM требуют дополнительных подходов.
Всплывающие окна в Google Maps API не создаются автоматически для
каждого маркера. Обычно используется один экземпляр
InfoWindow, который переиспользуется для нескольких
объектов. Такой подход снижает нагрузку на DOM и предотвращает
накопление лишних узлов.
const infoWindow = new google.maps.InfoWindow();
markers.forEach((marker) => {
marker.addListener("click", () => {
infoWindow.setContent(marker.title);
infoWindow.open({
anchor: marker,
map,
});
});
});
Ключевой момент — переопределение содержимого через
setContent перед каждым открытием.
InfoWindow поддерживает вставку HTML, включая формы,
изображения и кнопки. Однако важно учитывать, что контент рендерится в
отдельном DOM-контейнере, не связанном напрямую с основным деревом
приложения.
infoWindow.setContent(`
<div class="popup">
<h3>Заголовок</h3>
<p>Динамическое описание объекта</p>
<button id="actionBtn">Действие</button>
</div>
`);
Обработчики событий для элементов внутри InfoWindow
требуют делегирования после открытия окна:
google.maps.event.addListener(infoWindow, "domready", () => {
document.getElementById("actionBtn").addEventListener("click", () => {
console.log("Клик внутри всплывающего окна");
});
});
Особенность заключается в том, что DOM создаётся заново при каждом открытии, поэтому привязка событий должна повторяться.
Стандартный внешний вид ограничен встроенными стилями Google Maps. Изменение визуальной части достигается через обёртку HTML и переопределение CSS внутри контента.
infoWindow.setContent(`
<div class="custom-window">
<div class="header">Объект</div>
<div class="body">Информация</div>
</div>
`);
.custom-window {
font-family: Arial, sans-serif;
width: 200px;
}
.custom-window .header {
font-weight: bold;
margin-bottom: 5px;
}
Важно учитывать, что внешние стили страницы не всегда применяются одинаково предсказуемо из-за изоляции контента внутри контейнера InfoWindow.
Несмотря на удобство, InfoWindow имеет ряд архитектурных
ограничений:
Для сложных интерфейсов используется другой подход — пользовательские оверлеи.
OverlayView предоставляет доступ к низкоуровневому API,
позволяющему размещать произвольные DOM-элементы поверх карты с точным
контролем координат и отрисовки.
Базовая структура:
class CustomPopup extends google.maps.OverlayView {
constructor(position, content) {
super();
this.position = position;
this.content = content;
this.div = null;
}
onAdd() {
this.div = document.createElement("div");
this.div.className = "custom-popup";
this.div.innerHTML = this.content;
const panes = this.getPanes();
panes.floatPane.appendChild(this.div);
}
draw() {
const projection = this.getProjection();
const point = projection.fromLatLngToDivPixel(this.position);
if (this.div) {
this.div.style.left = point.x + "px";
this.div.style.top = point.y + "px";
}
}
onRemove() {
if (this.div) {
this.div.remove();
this.div = null;
}
}
}
Использование:
const popup = new CustomPopup(
{ lat: 52.4, lng: 71.5 },
"<div>Кастомный блок</div>"
);
popup.setMap(map);
OverlayView использует проекцию карты для преобразования
географических координат в экранные пиксели. Метод
fromLatLngToDivPixel возвращает координаты относительно
контейнера карты.
Ключевой момент заключается в том, что позиция должна пересчитываться при каждом изменении состояния карты:
Метод draw() вызывается автоматически системой
рендеринга.
Google Maps использует несколько слоёв (panes):
mapPane — базовая карта;overlayLayer — линии и полигоны;overlayMouseTarget — интерактивные элементы;floatPane — всплывающие элементы и окна.Для всплывающих окон обычно используется floatPane, так
как он обеспечивает максимальный приоритет отображения.
this.getPanes().floatPane.appendChild(this.div);
Пользовательские оверлеи не имеют встроенной логики событий карты, поэтому обработка взаимодействий выполняется вручную:
this.div.addEventListener("click", (e) => {
e.stopPropagation();
console.log("Клик по кастомному попапу");
});
Остановка всплытия событий критична, иначе карта будет интерпретировать клик как взаимодействие с поверхностью.
Так как DOM контролируется вручную, обновление содержимого выполняется напрямую:
this.div.querySelector(".content").textContent = "Новые данные";
При необходимости можно полностью пересоздавать DOM-структуру или применять виртуальные подходы обновления.
Закрытие кастомного оверлея реализуется вручную через
setMap(null):
popup.setMap(null);
При этом вызывается onRemove, где происходит очистка DOM
и освобождение памяти.
При работе с большим числом всплывающих окон важно учитывать:
draw() создают нагрузку на main
thread;floatPane могут становиться дорогими при
масштабировании;InfoWindow предпочтительнее
множественных экземпляров;Оптимизация часто достигается через:
В практических системах часто используется гибридный подход:
InfoWindow — для простых карточек и краткой
информации;OverlayView — для сложных интерактивных
интерфейсов;Такой подход позволяет балансировать между производительностью и гибкостью интерфейса, избегая перегрузки DOM и упрощая архитектуру слоя отображения поверх карты.