В рамках Google Maps JavaScript API информационное окно
(google.maps.InfoWindow) представляет собой всплывающий
контейнер, привязанный к карте или маркеру, предназначенный для
отображения произвольного HTML-содержимого. При работе с несколькими
объектами карты возникает необходимость управления множеством таких окон
одновременно, что требует аккуратной организации состояния и жизненного
цикла каждого экземпляра.
Архитектура API допускает создание неограниченного числа экземпляров
InfoWindow, однако практическое поведение интерфейса
зависит от стратегии управления: одновременное отображение,
переиспользование одного окна, или динамическое переключение между
окнами.
Каждое информационное окно создаётся как независимый объект:
const infoWindowA = new google.maps.InfoWindow({
content: '<div>Маркер A</div>'
});
const infoWindowB = new google.maps.InfoWindow({
content: '<div>Маркер B</div>'
});
Каждый экземпляр хранит собственное состояние, DOM-содержимое и
позиционирование. Привязка осуществляется через метод open,
который связывает окно с картой или конкретным маркером.
infoWindowA.open({
anchor: markerA,
map: map
});
infoWindowB.open({
anchor: markerB,
map: map
});
Одновременное отображение нескольких окон возможно без ограничений, однако визуально это часто приводит к перекрытиям и усложнению интерфейса.
При сценарии, где каждый маркер имеет собственное постоянное информационное окно, используется модель «один маркер — одно окно».
markers.forEach(({ position, text }) => {
const marker = new google.maps.Marker({
position,
map
});
const infoWindow = new google.maps.InfoWindow({
content: `<div>${text}</div>`
});
marker.addListener('click', () => {
infoWindow.open({
anchor: marker,
map
});
});
});
Такой подход характеризуется простотой, но увеличивает нагрузку при большом количестве маркеров из-за множества DOM-объектов.
Распространённой архитектурой является использование одного активного информационного окна для всей карты. В этом случае новое открытие автоматически закрывает предыдущее состояние.
const sharedInfoWindow = new google.maps.InfoWindow();
markers.forEach((marker, index) => {
marker.addListener('click', () => {
sharedInfoWindow.setContent(`Маркер ${index}`);
sharedInfoWindow.open({
anchor: marker,
map
});
});
});
Поведение обеспечивается переиспользованием одного экземпляра, что снижает нагрузку на рендеринг и упрощает управление состоянием.
При использовании нескольких независимых экземпляров требуется ручное управление их видимостью. Для этого применяется хранение ссылок и циклическое закрытие.
const infoWindows = [];
markers.forEach((marker, i) => {
const infoWindow = new google.maps.InfoWindow({
content: `Маркер ${i}`
});
infoWindows.push(infoWindow);
marker.addListener('click', () => {
infoWindows.forEach(w => w.close());
infoWindow.open({
anchor: marker,
map
});
});
});
Такой подход обеспечивает строгую модель «одно открытое окно в момент времени», сохраняя при этом независимость экземпляров.
При масштабных наборах данных используется связка структур:
const state = markers.map((marker, i) => ({
marker,
infoWindow: new google.maps.InfoWindow({
content: `Объект ${i}`
})
}));
state.forEach(({ marker, infoWindow }) => {
marker.addListener('click', () => {
state.forEach(s => s.infoWindow.close());
infoWindow.open({
anchor: marker,
map
});
});
});
Подобная модель удобна при синхронизации с внешними данными (например, API-ответами или состоянием приложения).
При одновременном открытии нескольких окон возникает проблема
перекрытия. Внутренний механизм рендеринга использует
z-index, который увеличивается при открытии окна, однако
порядок не всегда соответствует логике пользовательского
взаимодействия.
Для контроля используется:
close()Множественные окна часто требуют изменения контента без пересоздания экземпляра.
infoWindow.setContent(`
<div>
<h4>Обновлённые данные</h4>
<p>Время: ${Date.now()}</p>
</div>
`);
При использовании централизованного окна обновление содержимого становится ключевым механизмом переключения контекста между маркерами.
Информационные окна могут быть связаны не только с маркерами, но и с произвольными координатами карты:
map.addListener('click', (event) => {
sharedInfoWindow.setContent(
`Координаты: ${event.latLng.lat()}, ${event.latLng.lng()}`
);
sharedInfoWindow.setPosition(event.latLng);
sharedInfoWindow.open(map);
});
При множественных точках взаимодействия важно контролировать закрытие предыдущих окон, чтобы исключить накопление визуальных слоёв.
При работе с данными, поступающими из внешних источников, структура часто формируется на лету:
function renderLocations(locations) {
const windows = [];
locations.forEach(loc => {
const marker = new google.maps.Marker({
position: loc.position,
map
});
const infoWindow = new google.maps.InfoWindow({
content: `<strong>${loc.name}</strong>`
});
windows.push({ marker, infoWindow });
marker.addListener('click', () => {
windows.forEach(w => w.infoWindow.close());
infoWindow.open({
anchor: marker,
map
});
});
});
}
Такая схема обеспечивает согласованность между визуальными элементами и источником данных.
При масштабировании до сотен объектов возникают характерные ограничения:
InfoWindowОптимизация обычно достигается за счёт:
MarkerClusterer)Информационные окна не требуют явного удаления, однако их закрытие освобождает ресурсы визуального слоя. При динамическом удалении маркеров целесообразно также разрывать связи событий:
marker.setMap(null);
infoWindow.close();
При долгоживущих картах важно учитывать накопление неиспользуемых экземпляров и событийных слушателей.
Типовые модели управления множественными окнами включают:
Выбор модели определяется плотностью маркеров, частотой взаимодействий и требованиями к интерфейсу.
Информационные окна часто синхронизируются с состоянием интерфейса:
marker.addListener('mouseover', () => {
sharedInfoWindow.setContent('Hover-состояние');
sharedInfoWindow.open({ anchor: marker, map });
});
marker.addListener('mouseout', () => {
sharedInfoWindow.close();
});
Такая модель уменьшает количество одновременно активных окон и повышает предсказуемость поведения интерфейса.