Маркеры с HTML содержимым

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

Современный подход основан на классе AdvancedMarkerElement, который позволяет размещать внутри маркера полноценные HTML-элементы и применять к ним стили CSS.


Подключение библиотеки маркеров

Для работы с HTML-маркерами необходимо подключить библиотеку marker.

async function initMap() {
    const { Map } = await google.maps.importLibrary("maps");
    const { AdvancedMarkerElement } =
        await google.maps.importLibrary("marker");

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

После загрузки библиотеки становится доступен класс AdvancedMarkerElement, поддерживающий пользовательское содержимое.


Создание HTML-маркера

Основная идея заключается в том, что в качестве содержимого маркера используется DOM-элемент.

const markerElement = document.createElement("div");
markerElement.textContent = "Офис";

const marker = new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.751244,
        lng: 37.618423
    },
    content: markerElement
});

Вместо стандартной иконки на карте будет отображён HTML-элемент.


Использование сложной HTML-разметки

Внутри маркера может находиться произвольная структура.

const markerContent = document.createElement("div");

markerContent.innerHTML = `
    <div class="custom-marker">
        <div class="title">Главный офис</div>
        <div class="status">Открыт</div>
    </div>
`;

const marker = new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.75,
        lng: 37.61
    },
    content: markerContent
});

Такой подход позволяет строить полноценные интерфейсные компоненты непосредственно на карте.


Стилизация через CSS

Внешний вид маркера обычно полностью определяется CSS.

.custom-marker {
    background: white;
    border-radius: 12px;
    padding: 10px 14px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.25);
    border: 1px solid #ddd;
    min-width: 120px;
}

.custom-marker .title {
    font-weight: bold;
    margin-bottom: 4px;
}

.custom-marker .status {
    color: green;
    font-size: 13px;
}

Поскольку используется обычный HTML, доступны:

  • Flexbox;
  • Grid Layout;
  • CSS-анимации;
  • SVG;
  • псевдоэлементы;
  • CSS-переменные;
  • адаптивная вёрстка.

Фактически маркер становится полноценным веб-компонентом.


Маркер в виде ценника

Один из самых распространённых сценариев — отображение стоимости объекта недвижимости.

const element = document.createElement("div");

element.className = "price-marker";
element.textContent = "₽ 12 500 000";

new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.76,
        lng: 37.64
    },
    content: element
});

CSS:

.price-marker {
    background: #1976d2;
    color: white;
    padding: 8px 14px;
    border-radius: 20px;
    font-weight: 600;
    white-space: nowrap;
}

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


Использование изображений внутри маркера

HTML-маркер может содержать изображения.

const avatar = document.createElement("div");

avatar.innerHTML = `
    <img
        src="avatar.jpg"
        alt=""
        class="avatar"
    >
`;

new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.73,
        lng: 37.58
    },
    content: avatar
});

CSS:

.avatar {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: 3px solid white;
    object-fit: cover;
}

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


Добавление пользовательской формы указателя

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

<div class="marker">
    <div class="card">
        Магазин
    </div>
    <div class="pointer"></div>
</div>
.marker {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.card {
    background: white;
    padding: 10px 15px;
    border-radius: 10px;
    box-shadow: 0 2px 10px rgba(0,0,0,.2);
}

.pointer {
    width: 14px;
    height: 14px;
    background: white;
    transform: rotate(45deg);
    margin-top: -7px;
}

В результате создаётся визуальный эффект классического указателя местоположения.


Интерактивные элементы внутри маркера

Поскольку содержимое является DOM-элементом, к нему можно привязывать обработчики событий.

const element = document.createElement("div");

element.innerHTML = `
    <button class="details-btn">
        Подробнее
    </button>
`;

element
    .querySelector(".details-btn")
    .addEventListener("click", () => {
        console.log("Нажата кнопка");
    });

new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.74,
        lng: 37.62
    },
    content: element
});

Поддерживаются любые события браузера:

  • click;
  • dblclick;
  • mouseenter;
  • mouseleave;
  • input;
  • change;
  • focus;
  • blur.

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


Создание маркера через шаблонную функцию

При большом количестве объектов удобно использовать фабрику компонентов.

function createMarkerContent(title, status) {
    const element = document.createElement("div");

    element.innerHTML = `
        <div class="location-card">
            <h4>${title}</h4>
            <span>${status}</span>
        </div>
    `;

    return element;
}

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

new AdvancedMarkerElement({
    map,
    position: location,
    content: createMarkerContent(
        "Склад №1",
        "Работает"
    )
});

Такой подход делает код более масштабируемым.


Генерация маркеров из массива данных

Практически все реальные проекты получают координаты из базы данных или API.

const offices = [
    {
        lat: 55.75,
        lng: 37.61,
        title: "Офис 1"
    },
    {
        lat: 55.77,
        lng: 37.64,
        title: "Офис 2"
    },
    {
        lat: 55.73,
        lng: 37.58,
        title: "Офис 3"
    }
];

offices.forEach(item => {
    const element = document.createElement("div");

    element.className = "office-marker";
    element.textContent = item.title;

    new AdvancedMarkerElement({
        map,
        position: {
            lat: item.lat,
            lng: item.lng
        },
        content: element
    });
});

Изменение содержимого после создания

DOM-элемент остаётся доступным после создания маркера.

const content = document.createElement("div");
content.textContent = "Загрузка...";

const marker = new AdvancedMarkerElement({
    map,
    position: location,
    content
});

setTimeout(() => {
    content.textContent = "Данные получены";
}, 3000);

Это удобно при работе с асинхронными запросами.


Анимация HTML-маркеров

Поскольку используется обычный HTML, анимация реализуется средствами CSS.

.marker-pulse {
    animation: pulse 1.5s infinite;
}

@keyframes pulse {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.15);
    }

    100% {
        transform: scale(1);
    }
}

Применение:

element.classList.add("marker-pulse");

Возможны любые CSS-анимации:

  • пульсация;
  • вращение;
  • плавное появление;
  • изменение цвета;
  • подпрыгивание;
  • мигание индикатора.

Использование SVG внутри HTML-маркера

Вместо изображений можно использовать встроенный SVG.

const element = document.createElement("div");

element.innerHTML = `
<svg
    width="40"
    height="40"
    viewBox="0 0 40 40"
>
    <circle
        cx="20"
        cy="20"
        r="18"
        fill="#ff5722"
    />
</svg>
`;

Преимущества SVG:

  • отсутствие потери качества;
  • небольшие размеры файлов;
  • лёгкая стилизация через CSS;
  • возможность анимации отдельных элементов.

Управление видимостью маркера

HTML-маркер можно скрывать и показывать через стили.

content.style.display = "none";

Показ:

content.style.display = "block";

Также часто применяется управление через CSS-классы.

.hidden {
    opacity: 0;
    pointer-events: none;
}
content.classList.add("hidden");

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

События могут обрабатываться как через DOM, так и через API карты.

marker.addListener("click", () => {
    console.log("Клик по маркеру");
});

Либо через элементы интерфейса внутри содержимого.

content.addEventListener("click", event => {
    console.log(event.target);
});

Выбор зависит от требуемой логики приложения.


Комбинирование HTML-маркера и InfoWindow

HTML-маркер может использоваться совместно с информационным окном.

const infoWindow = new google.maps.InfoWindow({
    content: "<h3>Информация об объекте</h3>"
});

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

Такой подход позволяет показывать краткую информацию в маркере и подробную — во всплывающем окне.


Производительность при большом количестве HTML-маркеров

HTML-маркеры значительно тяжелее стандартных иконок, поскольку каждый из них представляет отдельное DOM-дерево.

При работе с сотнями и тысячами объектов рекомендуется:

  • минимизировать вложенность HTML;
  • избегать сложных теней и фильтров;
  • не использовать тяжёлые CSS-анимации;
  • удалять невидимые маркеры;
  • применять кластеризацию;
  • использовать переиспользуемые шаблоны компонентов;
  • уменьшать количество обработчиков событий.

Для очень больших наборов данных стандартные маркеры или специализированные решения визуализации могут оказаться эффективнее.


Практический пример карточки объекта

function createLocationCard(data) {
    const element = document.createElement("div");

    element.innerHTML = `
        <div class="location-card">
            <div class="name">
                ${data.name}
            </div>

            <div class="address">
                ${data.address}
            </div>

            <div class="rating">
                ★ ${data.rating}
            </div>
        </div>
    `;

    return element;
}

new AdvancedMarkerElement({
    map,
    position: {
        lat: 55.751244,
        lng: 37.618423
    },
    content: createLocationCard({
        name: "Кофейня",
        address: "ул. Центральная, 10",
        rating: 4.8
    })
});
.location-card {
    background: white;
    padding: 12px;
    border-radius: 12px;
    min-width: 180px;
    box-shadow: 0 4px 16px rgba(0,0,0,.15);
}

.location-card .name {
    font-weight: 700;
    margin-bottom: 6px;
}

.location-card .address {
    font-size: 13px;
    color: #666;
    margin-bottom: 6px;
}

.location-card .rating {
    color: #f57c00;
}

Подобная структура позволяет превращать обычные географические точки в полноценные визуальные компоненты интерфейса, отображающие сложные данные, реагирующие на действия пользователя и полностью контролируемые средствами HTML, CSS и JavaScript.