Привязка InfoWindow к маркерам

Google Maps JavaScript API предоставляет механизм отображения информационных окон (InfoWindow), которые связываются с маркерами карты и позволяют выводить контекстные данные по объектам геопространства. Взаимодействие между Marker и InfoWindow реализуется через событийную модель, где открытие окна информации происходит в ответ на пользовательские действия или программные события.

Базовая модель взаимодействия Marker и InfoWindow

Каждый маркер в API представляет собой объект, связанный с координатами и визуальным отображением на карте. InfoWindow — отдельный объект, содержащий HTML-контент и позиционируемый относительно карты или конкретного маркера.

Связь между ними устанавливается через метод open, который принимает карту и маркер как аргументы:

const infoWindow = new google.maps.InfoWindow({
  content: '<div>Точка интереса</div>'
});

const marker = new google.maps.Marker({
  position: { lat: 55.751244, lng: 37.618423 },
  map: map,
  title: 'Москва'
});

marker.addListener('click', () => {
  infoWindow.open({
    anchor: marker,
    map: map,
    shouldFocus: false
  });
});

В данной модели anchor определяет объект привязки. Использование маркера в качестве якоря обеспечивает автоматическое позиционирование InfoWindow над соответствующей точкой.

Привязка одного InfoWindow к множеству маркеров

Распространённый подход заключается в использовании единственного экземпляра InfoWindow для всех маркеров. Это снижает нагрузку на DOM и предотвращает дублирование окон.

const infoWindow = new google.maps.InfoWindow();

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

  marker.addListener('click', () => {
    infoWindow.setContent(`
      <div>
        <h3>${location.name}</h3>
        <p>${location.description}</p>
      </div>
    `);

    infoWindow.open({
      anchor: marker,
      map: map
    });
  });

  return marker;
});

При каждом клике содержимое окна обновляется методом setContent, а позиция автоматически синхронизируется с активным маркером.

Управление состоянием InfoWindow

InfoWindow не закрывается автоматически при открытии нового окна, если используется несколько экземпляров. При работе с единым экземпляром поведение упрощается, поскольку новое открытие переопределяет предыдущее состояние.

Закрытие осуществляется через метод:

infoWindow.close();

Также возможно отслеживание состояния через событие closeclick:

infoWindow.addListener('closeclick', () => {
  console.log('InfoWindow закрыт');
});

Динамическое обновление содержимого

Содержимое InfoWindow может изменяться без его закрытия. Это используется при асинхронной загрузке данных или обновлении информации по событию.

marker.addListener('click', async () => {
  infoWindow.setContent('<div>Загрузка...</div>');
  infoWindow.open({ anchor: marker, map });

  const data = await fetch(`/api/location/${marker.id}`).then(r => r.json());

  infoWindow.setContent(`
    <div>
      <strong>${data.name}</strong>
      <p>${data.details}</p>
    </div>
  `);
});

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

Привязка InfoWindow к кастомным маркерам

В случаях использования кастомных маркеров, созданных через OverlayView, привязка InfoWindow требует явного указания позиции через объект LatLng:

infoWindow.open({
  map: map,
  position: { lat: 55.751244, lng: 37.618423 }
});

Отсутствие anchor компенсируется прямым указанием координат, что актуально для нестандартных визуальных слоёв.

Синхронизация нескольких окон и логика переключения

При работе с множеством маркеров часто применяется паттерн единственного активного InfoWindow. Перед открытием нового окна предыдущее закрывается автоматически:

let activeInfoWindow = null;

markers.forEach((marker, index) => {
  marker.addListener('click', () => {
    if (activeInfoWindow) {
      activeInfoWindow.close();
    }

    infoWindow.setContent(`<div>${locations[index].name}</div>`);
    infoWindow.open({ anchor: marker, map });

    activeInfoWindow = infoWindow;
  });
});

Такая логика исключает перекрытие интерфейса и упрощает управление состоянием отображения.

Использование HTML и событий внутри InfoWindow

Контент InfoWindow поддерживает полноценный HTML, включая обработчики событий, однако после повторного открытия содержимое пересоздаётся, что требует повторной инициализации интерактивных элементов.

infoWindow.setContent(`
  <div id="iw-content">
    <button id="action-btn">Действие</button>
  </div>
`);

infoWindow.addListener('domready', () => {
  document.getElementById('action-btn').addEventListener('click', () => {
    console.log('Кнопка нажата');
  });
});

Событие domready гарантирует, что DOM внутри InfoWindow уже отрисован и доступен для манипуляций.

Смещение и позиционирование InfoWindow

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

const infoWindow = new google.maps.InfoWindow({
  content: 'Контент',
  pixelOffset: new google.maps.Size(0, -30)
});

Это особенно важно при использовании кастомных иконок маркеров с нестандартными размерами.

Поведение при масштабировании и перемещении карты

InfoWindow автоматически синхронизирует своё положение при изменении центра карты или масштаба. Привязка к anchor обеспечивает постоянную актуализацию позиции без необходимости ручного пересчёта координат.

При использовании фиксированной позиции (position) требуется ручное управление обновлением при изменении состояния карты, если связанный объект перемещается логически.

Особенности жизненного цикла InfoWindow

Объект InfoWindow сохраняет состояние до явного удаления или замены. Повторное использование одного экземпляра считается оптимальной практикой для снижения накладных расходов. При этом важно учитывать, что содержимое и слушатели событий не сохраняются между открытиями, если они пересоздаются динамически.

Поведение жизненного цикла включает:

  • инициализацию с базовым HTML;
  • открытие через open;
  • обновление через setContent;
  • закрытие через close;
  • повторное открытие с новым контекстом.