MarkerWithLabel

В классическом Google Maps JavaScript API стандартный объект маркера предоставляет ограниченные возможности для отображения текста рядом с иконкой. Свойство label позволяет вывести короткую подпись, но оно не подходит для сложной верстки, стилизации и динамического контента. Для решения этих ограничений используется расширение MarkerWithLabel, позволяющее привязывать произвольный HTML-контент к маркеру карты.

Данный подход был особенно востребован в версиях API до появления AdvancedMarkerElement, когда требовалось создавать интерактивные подписи, стилизованные бейджи, числовые метки, кастомные подписи объектов и элементы UI поверх карты.


Архитектура MarkerWithLabel

MarkerWithLabel реализуется как надстройка над стандартным google.maps.Marker. Внутри он:

  • создаёт DOM-элемент для метки
  • синхронизирует его позицию с маркером
  • управляет слоями отображения (overlay)
  • обрабатывает события карты (pan, zoom, drag)

Ключевая идея заключается в разделении:

  • географическая позиция — управляется API карты
  • визуальная подпись — управляется HTML/CSS слоем

Подключение библиотеки

MarkerWithLabel не входит в основной пакет Google Maps JavaScript API и подключается отдельно.

Типичный способ установки через npm:

npm install @googlemaps/markerwithlabel

Далее импорт:

import MarkerWithLabel from "@googlemaps/markerwithlabel";

При использовании без сборщиков возможно подключение через CDN или локальную копию скрипта (в старых проектах).


Базовая инициализация

Для работы требуется уже созданная карта:

const map = new google.maps.Map(document.getElementById("map"), {
  center: { lat: 55.751244, lng: 37.618423 },
  zoom: 10,
});

Создание маркера с меткой:

const marker = new MarkerWithLabel({
  position: { lat: 55.751244, lng: 37.618423 },
  map: map,
  labelContent: "Москва",
  labelAnchor: new google.maps.Point(50, 0),
  labelClass: "map-label",
  labelStyle: { opacity: 0.9 },
});

Основные параметры конфигурации

labelContent

Определяет содержимое метки. Поддерживается HTML.

labelContent: "<div>Центр города</div>"

Использование HTML позволяет:

  • вставлять иконки
  • добавлять несколько строк текста
  • применять сложную структуру

labelClass

CSS-класс, применяемый к контейнеру метки.

labelClass: "custom-marker-label"

Пример CSS:

.custom-marker-label {
  background: white;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: #333;
  box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}

labelAnchor

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

labelAnchor: new google.maps.Point(0, 0)

Это важно для корректного позиционирования, особенно при нестандартных иконках.


labelStyle

Позволяет задать inline-стили:

labelStyle: {
  fontSize: "14px",
  fontWeight: "bold",
  color: "#000",
}

Пример сложной метки с HTML

MarkerWithLabel позволяет создавать полноценные UI-элементы:

const marker = new MarkerWithLabel({
  position: { lat: 55.76, lng: 37.64 },
  map: map,
  labelContent: `
    <div class="price-tag">
      <div class="price">1200 ₽</div>
      <div class="desc">квартира</div>
    </div>
  `,
  labelClass: "price-label",
  labelAnchor: new google.maps.Point(40, 40),
});

CSS:

.price-label {
  pointer-events: none;
}

.price-tag {
  background: #1e88e5;
  color: white;
  padding: 8px 12px;
  border-radius: 8px;
  text-align: center;
  min-width: 80px;
}

Обработка событий

MarkerWithLabel поддерживает события маркера Google Maps, включая:

  • click
  • drag
  • mouseover
  • mouseout

Пример:

marker.addListener("click", () => {
  console.log("Маркер нажат");
});

Для интерактивных меток важно учитывать pointer-events в CSS, иначе события могут не доходить до карты.


Работа с z-index и слоями

При большом количестве маркеров возникает проблема перекрытия. MarkerWithLabel позволяет управлять порядком отображения:

zIndex: 1000

Логика:

  • более высокий zIndex отображается поверх других
  • динамическое изменение позволяет выделять активные элементы

Drag и динамическое обновление

MarkerWithLabel поддерживает перетаскивание:

draggable: true

Обновление позиции метки происходит автоматически, но при кастомной логике можно слушать событие:

marker.addListener("dragend", (event) => {
  console.log(event.latLng.lat(), event.latLng.lng());
});

Ограничения MarkerWithLabel

Несмотря на гибкость, библиотека имеет ряд ограничений:

  • не входит в официальный core API
  • требует дополнительного подключения
  • может конфликтовать с новыми версиями Google Maps
  • не оптимизирована для высокой плотности маркеров
  • отсутствует активное развитие в пользу новых механизмов

Переход к AdvancedMarkerElement

Современная замена MarkerWithLabel — AdvancedMarkerElement, который предоставляет:

  • нативную поддержку HTML-элементов
  • лучшую производительность
  • официальную поддержку Google
  • более гибкую систему кастомизации

Пример современного подхода:

const marker = new google.maps.marker.AdvancedMarkerElement({
  map,
  position: { lat: 55.75, lng: 37.61 },
  content: document.createElement("div"),
});

Сравнение подходов

MarkerWithLabel:

  • сторонняя библиотека
  • основан на overlay
  • гибкий, но устаревающий подход

AdvancedMarkerElement:

  • встроенный API
  • нативная работа с DOM
  • лучшая производительность и поддержка

Работа с динамическими списками маркеров

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

const points = [
  { lat: 55.75, lng: 37.61, title: "A" },
  { lat: 55.76, lng: 37.62, title: "B" },
];

points.forEach((p) => {
  new MarkerWithLabel({
    position: { lat: p.lat, lng: p.lng },
    map,
    labelContent: p.title,
    labelClass: "simple-label",
  });
});

В таких сценариях важно контролировать:

  • очистку старых маркеров
  • переиспользование объектов карты
  • минимизацию DOM-узлов

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

При большом количестве меток критично учитывать:

  • уменьшение количества DOM-элементов
  • использование кластеризации маркеров
  • ограничение сложного HTML внутри labelContent
  • отключение лишних обработчиков событий

Кластеризация обычно реализуется через отдельные библиотеки и не зависит напрямую от MarkerWithLabel, но архитектурно применяется совместно.


Типичные ошибки при использовании

  • неправильный labelAnchor, приводящий к смещению текста
  • отсутствие CSS-ограничений ширины, из-за чего метки “ломают” карту
  • конфликт pointer-events и событий карты
  • утечка памяти при динамическом создании маркеров без удаления
  • смешивание старого API и AdvancedMarkerElement в одном проекте

Практическая модель использования в реальных проектах

MarkerWithLabel чаще всего применяется в:

  • картах недвижимости (цены объектов)
  • логистических системах (статусы точек)
  • туристических сервисах (названия объектов)
  • дашбордах мониторинга
  • игровых и интерактивных картах

Его основная ценность заключается в возможности превращать маркер из простой точки в полноценный UI-компонент, связанный с географическими координатами.