Попапы (Popup) используются для отображения дополнительной информации поверх карты. Чаще всего они привязываются к объектам слоёв и показываются при клике, наведении курсора или другом событии взаимодействия.
Типичные сценарии использования:
Попап представляет собой отдельный HTML-элемент, который автоматически позиционируется относительно географических координат.
Базовый экземпляр создаётся через конструктор
maplibregl.Popup().
const popup = new maplibregl.Popup()
.setLngLat([37.6176, 55.7558])
.setHTML('<h3>Москва</h3><p>Столица России</p>')
.addTo(map);
Основные методы:
| Метод | Назначение |
|---|---|
setLngLat() |
Установка координат |
setHTML() |
Вставка HTML |
setText() |
Вставка обычного текста |
setDOMContent() |
Передача DOM-элемента |
addTo() |
Добавление на карту |
remove() |
Удаление попапа |
После выполнения кода на карте появится окно в заданной точке.
Наиболее распространённый вариант — открытие окна после выбора объекта слоя.
Предположим, существует слой городов:
map.addLayer({
id: 'cities',
type: 'circle',
source: 'cities-source',
paint: {
'circle-radius': 6,
'circle-color': '#007cbf'
}
});
Для отображения информации используется обработчик события:
map.on('click', 'cities', (e) => {
const feature = e.features[0];
new maplibregl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(`
<h3>${feature.properties.name}</h3>
<p>Население: ${feature.properties.population}</p>
`)
.addTo(map);
});
Механизм работы:
e.features извлекается объект GeoJSON.Внутри обработчика доступны все свойства GeoJSON-объекта.
Пример структуры:
{
"type": "Feature",
"properties": {
"name": "Москва",
"population": 13000000,
"country": "Россия"
},
"geometry": {
"type": "Point",
"coordinates": [37.6176, 55.7558]
}
}
Доступ к данным:
map.on('click', 'cities', (e) => {
const properties = e.features[0].properties;
console.log(properties.name);
console.log(properties.population);
console.log(properties.country);
});
Эти значения обычно выводятся внутри попапа.
Если отображается только текст, безопаснее использовать
setText().
new maplibregl.Popup()
.setLngLat([37.6176, 55.7558])
.setText('Москва')
.addTo(map);
Преимущества:
Для форматированного содержимого применяется
setHTML().
new maplibregl.Popup()
.setLngLat([37.6176, 55.7558])
.setHTML(`
<h2>Москва</h2>
<p>Население: 13 млн человек</p>
`)
.addTo(map);
Внутри HTML можно размещать:
Пример:
new maplibregl.Popup()
.setLngLat([37.6176, 55.7558])
.setHTML(`
<div class="city-card">
<img src="city.jpg" width="200">
<h3>Москва</h3>
<p>Крупнейший город страны</p>
</div>
`)
.addTo(map);
Для сложных интерфейсов удобнее использовать реальные DOM-элементы.
const container = document.createElement('div');
const title = document.createElement('h3');
title.textContent = 'Москва';
const description = document.createElement('p');
description.textContent = 'Столица России';
container.appendChild(title);
container.appendChild(description);
new maplibregl.Popup()
.setLngLat([37.6176, 55.7558])
.setDOMContent(container)
.addTo(map);
Преимущества такого подхода:
Иногда требуется показывать информацию без клика.
let popup;
map.on('mouseenter', 'cities', (e) => {
popup = new maplibregl.Popup({
closeButton: false,
closeOnClick: false
});
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(`
<strong>
${e.features[0].properties.name}
</strong>
`)
.addTo(map);
});
Закрытие:
map.on('mouseleave', 'cities', () => {
if (popup) {
popup.remove();
}
});
Такой подход часто используется для подсказок (tooltip).
Для улучшения UX обычно меняется курсор.
map.on('mouseenter', 'cities', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'cities', () => {
map.getCanvas().style.cursor = '';
});
Пользователь сразу понимает, что объект интерактивен.
Конструктор принимает объект конфигурации.
const popup = new maplibregl.Popup({
closeButton: true,
closeOnClick: true
});
Наиболее важные параметры:
| Параметр | Назначение |
|---|---|
closeButton |
Показывать кнопку закрытия |
closeOnClick |
Закрывать при клике по карте |
anchor |
Положение относительно точки |
offset |
Смещение окна |
maxWidth |
Максимальная ширина |
focusAfterOpen |
Переводить фокус в окно |
Параметр anchor задаёт сторону привязки.
new maplibregl.Popup({
anchor: 'top'
});
Допустимые значения:
top
bottom
left
right
top-left
top-right
bottom-left
bottom-right
center
Пример:
new maplibregl.Popup({
anchor: 'bottom'
});
Окно будет располагаться над точкой.
Для тонкой настройки используется offset.
new maplibregl.Popup({
offset: 20
});
Также допускается объект:
new maplibregl.Popup({
offset: {
top: [0, 20],
bottom: [0, -20]
}
});
Особенно полезно при работе с крупными маркерами.
По умолчанию ширина ограничена.
Изменение:
new maplibregl.Popup({
maxWidth: '500px'
});
Либо полное снятие ограничения:
new maplibregl.Popup({
maxWidth: 'none'
});
Создание нового экземпляра при каждом клике может быть неэффективным.
Лучше использовать один объект.
const popup = new maplibregl.Popup();
map.on('click', 'cities', (e) => {
const feature = e.features[0];
popup
.setLngLat(feature.geometry.coordinates)
.setHTML(`
<h3>${feature.properties.name}</h3>
`)
.addTo(map);
});
Преимущества:
Для полигонов координаты точки отображения обычно берутся из места клика.
map.on('click', 'regions', (e) => {
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`
<h3>${e.features[0].properties.name}</h3>
`)
.addTo(map);
});
Использование e.lngLat позволяет показывать окно именно
там, где произошло взаимодействие.
Для линейных объектов используется аналогичный подход.
map.on('click', 'roads', (e) => {
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`
<p>
Дорога:
${e.features[0].properties.name}
</p>
`)
.addTo(map);
});
Поскольку линия содержит множество координат, позиция клика обычно оказывается наиболее удобной.
Попап может загружать информацию после открытия.
map.on('click', 'cities', async (e) => {
const popup = new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML('Загрузка...')
.addTo(map);
const response = await fetch(
`/api/city/${e.features[0].properties.id}`
);
const data = await response.json();
popup.setHTML(`
<h3>${data.name}</h3>
<p>${data.description}</p>
`);
});
Такой подход позволяет не хранить большой объём данных непосредственно в GeoJSON.
Стандартный внешний вид можно полностью изменить через CSS.
.maplibregl-popup-content {
padding: 20px;
border-radius: 12px;
}
.maplibregl-popup-content h3 {
margin-top: 0;
}
Изменение стрелки:
.maplibregl-popup-tip {
border-top-color: #ffffff;
}
Изменение тени:
.maplibregl-popup-content {
box-shadow:
0 10px 30px rgba(0,0,0,0.2);
}
Для индивидуального оформления используется метод
addClassName().
const popup = new maplibregl.Popup();
popup.addClassName('city-popup');
CSS:
.city-popup .maplibregl-popup-content {
background: #f8f8f8;
border: 2px solid #007cbf;
}
Удаление класса:
popup.removeClassName('city-popup');
Проверка:
popup.toggleClassName('city-popup');
Удаление окна:
popup.remove();
Проверка существования:
if (popup.isOpen()) {
popup.remove();
}
Полезно при переключении режимов карты или смене источников данных.
Экземпляр Popup поддерживает собственные события.
Открытие:
popup.on('open', () => {
console.log('Попап открыт');
});
Закрытие:
popup.on('close', () => {
console.log('Попап закрыт');
});
Это позволяет выполнять дополнительную логику при изменении состояния окна.
Частая проблема — появление множества окон после серии кликов.
Решение:
let popup;
map.on('click', 'cities', (e) => {
if (popup) {
popup.remove();
}
popup = new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`
<h3>${e.features[0].properties.name}</h3>
`)
.addTo(map);
});
На карте всегда остаётся только один активный попап.
const popup = new maplibregl.Popup({
maxWidth: '350px'
});
map.on('click', 'cities', (e) => {
const city = e.features[0].properties;
popup
.setLngLat(e.lngLat)
.setHTML(`
<div class="city-info">
<h2>${city.name}</h2>
<table>
<tr>
<td>Страна</td>
<td>${city.country}</td>
</tr>
<tr>
<td>Население</td>
<td>${city.population}</td>
</tr>
</table>
</div>
`)
.addTo(map);
});
Подобная структура часто применяется в геоинформационных системах, туристических сервисах, каталогах объектов недвижимости, логистических платформах и аналитических картографических приложениях, где слой содержит большое количество объектов с набором связанных атрибутов. Попап становится основным инструментом отображения детальной информации без перегрузки карты дополнительными элементами интерфейса.