Popup в Mapbox GL JS представляет собой всплывающий контейнер, который отображает произвольное содержимое поверх карты. Основная гибкость этого компонента заключается в том, что содержимое может быть как простым текстом, так и сложной HTML-разметкой, динамически формируемой на основе данных карты.
Popup поддерживает два основных метода установки содержимого: текстовый и HTML.
Метод setText используется для отображения безопасного
строкового контента без интерпретации HTML.
const popup = new mapboxgl.Popup()
.setLngLat([30.5, 50.5])
.setText('Простой текст внутри popup')
.addTo(map);
Особенности текстового режима:
Метод setHTML позволяет задавать полноценную разметку,
включая вложенные элементы, стили и структуру.
const popup = new mapboxgl.Popup()
.setLngLat([30.5, 50.5])
.setHTML(`
<div class="popup-content">
<h3>Объект</h3>
<p>Описание объекта на карте</p>
</div>
`)
.addTo(map);
Использование HTML расширяет возможности отображения:
Альтернативой строковому HTML является создание DOM-узлов вручную. Этот подход снижает риск ошибок разметки и повышает контроль над структурой.
const container = document.createElement('div');
container.className = 'popup-container';
const title = document.createElement('h3');
title.textContent = 'Название объекта';
const description = document.createElement('p');
description.textContent = 'Подробное описание';
container.appendChild(title);
container.appendChild(description);
const popup = new mapboxgl.Popup()
.setLngLat([30.5, 50.5])
.setDOMContent(container)
.addTo(map);
Метод setDOMContent особенно полезен при сложной логике
формирования интерфейса.
Часто popup формируется на основе свойств GeoJSON-объектов или данных слоя.
map.on('click', 'points-layer', (e) => {
const feature = e.features[0];
new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(`
<div>
<strong>${feature.properties.name}</strong>
<p>${feature.properties.description}</p>
</div>
`)
.addTo(map);
});
Ключевой принцип — связывание feature.properties с
шаблоном отображения.
При работе с большим количеством объектов целесообразно вынести генерацию содержимого в отдельную функцию.
function createPopupContent(properties) {
return `
<div class="popup-card">
<h4>${properties.title}</h4>
<p>${properties.text}</p>
<span>Рейтинг: ${properties.rating}</span>
</div>
`;
}
map.on('click', (e) => {
const feature = e.features[0];
new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(createPopupContent(feature.properties))
.addTo(map);
});
Такой подход упрощает поддержку и масштабирование интерфейса.
Popup может формироваться на основе данных, полученных из внешних API.
map.on('click', async (e) => {
const feature = e.features[0];
const response = await fetch(`/api/object/${feature.properties.id}`);
const data = await response.json();
new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(`
<div>
<h3>${data.name}</h3>
<p>${data.details}</p>
</div>
`)
.addTo(map);
});
В таких сценариях учитывается задержка загрузки и возможные состояния ошибки.
Использование setHTML требует контроля входных данных,
поскольку прямое внедрение строк может привести к XSS-уязвимостям.
Типовые меры:
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
}
.setHTML(`
<div>
<p>${escapeHtml(feature.properties.comment)}</p>
</div>
`)
Содержимое popup не ограничивается структурой — важную роль играет CSS-оформление.
.popup-card {
font-family: sans-serif;
padding: 10px;
max-width: 200px;
}
.popup-card h4 {
margin: 0 0 5px;
font-size: 14px;
}
.popup-card p {
margin: 0;
font-size: 12px;
color: #555;
}
Mapbox GL JS добавляет собственную обёртку
.mapboxgl-popup-content, которую можно переопределять:
.mapboxgl-popup-content {
border-radius: 8px;
padding: 12px;
}
HTML-содержимое позволяет размещать элементы управления.
.setHTML(`
<div>
<h4>Объект</h4>
<button oncl ick="handleClick()">Действие</button>
</div>
`)
Более корректный подход — привязка событий после добавления popup в DOM:
const popup = new mapboxgl.Popup()
.setLngLat(coords)
.setHTML(`
<div>
<button class="action-btn">Нажать</button>
</div>
`)
.addTo(map);
popup.getElement().querySelector('.action-btn')
.addEventListener('click', () => {
console.log('Событие обработано');
});
Для оптимизации можно переиспользовать DOM-узлы или кэшировать шаблоны.
const popupCache = new Map();
function getPopupContent(id, properties) {
if (popupCache.has(id)) return popupCache.get(id);
const el = document.createElement('div');
el.innerHTML = `<strong>${properties.name}</strong>`;
popupCache.set(id, el);
return el;
}
При большом количестве объектов следует избегать частого создания сложных DOM-структур.
Рекомендации:
setText при отсутствии необходимости в
структуреconst popup = new mapboxgl.Popup();
map.on('mousemove', (e) => {
const feature = map.queryRenderedFeatures(e.point)[0];
if (!feature) return;
popup
.setLngLat(feature.geometry.coordinates)
.setText(feature.properties.name)
.addTo(map);
});
Popup может отражать состояние карты в момент взаимодействия: масштаб, фильтры слоёв, видимые объекты.
map.on('click', (e) => {
const zoom = map.getZoom();
new mapboxgl.Popup()
.setLngLat(e.lngLat)
.setHTML(`<p>Текущий масштаб: ${zoom}</p>`)
.addTo(map);
});
Такой подход позволяет формировать контекстно-зависимые интерфейсы поверх карты.