HTML-контент в Mapbox GL JS используется как способ интеграции DOM-структур в карту: всплывающие окна, пользовательские маркеры, интерактивные карточки объектов и любые элементы интерфейса, которые невозможно выразить через слой canvas/WebGL. Библиотека Mapbox GL JS изначально рендерит карту через WebGL, но предоставляет мост к DOM через специальные API, позволяя вставлять HTML-структуры поверх карты без потери синхронизации координат.
Popup — основной механизм отображения HTML-контента, привязанного к координате.
const popup = new mapboxgl.Popup({
closeButton: true,
closeOnClick: false
});
popup.setHTML(`
<div class="popup-card">
<h3>Объект на карте</h3>
<p>Описание точки интереса</p>
</div>
`);
popup.setLngLat([71.4304, 51.1284]).addTo(map);
Метод setHTML принимает строку HTML и вставляет её
внутрь контейнера попапа. При этом содержимое не проходит автоматическую
очистку, поэтому управление безопасностью ложится на разработчика.
Альтернативный метод — setDOMContent, позволяющий
использовать уже созданный DOM-узел:
const container = document.createElement('div');
container.className = 'popup-card';
const title = document.createElement('h3');
title.textContent = 'Объект на карте';
const description = document.createElement('p');
description.textContent = 'Описание точки интереса';
container.appendChild(title);
container.appendChild(description);
popup.setDOMContent(container);
Использование setDOMContent предпочтительно при
динамических интерфейсах, так как позволяет избегать повторного парсинга
HTML и упрощает работу с событиями.
HTML-инъекции в popups — одна из частых проблем при работе с картами.
Поскольку setHTML напрямую вставляет строку в DOM, любое
неподготовленное содержимое может привести к XSS-уязвимостям.
Типовой подход к защите:
innerHTML в пользу DOM-методовfunction escapeHTML(str) {
return str.replace(/[&<>"']/g, function (m) {
return ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m];
});
}
popup.setHTML(`
<div>
<h3>${escapeHTML(userTitle)}</h3>
</div>
`);
Mapbox GL JS позволяет создавать маркеры на основе произвольных DOM-узлов. Это ключевой механизм для визуально сложных интерфейсов на карте.
const el = document.createElement('div');
el.className = 'custom-marker';
el.innerHTML = `
<div class="marker-content">
<span class="marker-icon"></span>
</div>
`;
new mapboxgl.Marker(el)
.setLngLat([71.4304, 51.1284])
.addTo(map);
HTML-маркер существует как полноценный DOM-элемент, который можно стилизовать через CSS:
.custom-marker {
width: 30px;
height: 30px;
background: white;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}
.marker-icon {
display: block;
width: 100%;
height: 100%;
background: url(icon.svg) center/contain no-repeat;
}
Главная особенность HTML-маркеров — их производительность. При большом количестве точек (сотни и тысячи) DOM-узлы становятся тяжёлыми, поэтому такие маркеры применяются только для ограниченных наборов данных.
DOM-маркеры и popups поддерживают стандартную модель событий браузера. Это позволяет строить интерактивные компоненты прямо внутри карты.
const el = document.createElement('div');
el.className = 'marker';
el.innerHTML = `
<button class="info-btn">Подробнее</button>
`;
el.querySelector('.info-btn').addEventListener('click', () => {
console.log('Открытие карточки объекта');
});
При динамическом создании большого количества маркеров предпочтительнее использовать делегирование событий:
el.addEventListener('click', (e) => {
if (e.target.classList.contains('info-btn')) {
console.log('Клик по кнопке внутри маркера');
}
});
HTML-слои часто синхронизируются с состоянием карты: масштабом, центром, выбранными объектами.
map.on('move', () => {
const center = map.getCenter();
document.getElementById('center-info').innerHTML = `
Широта: ${center.lat.toFixed(4)}<br>
Долгота: ${center.lng.toFixed(4)}
`;
});
Такой подход позволяет связывать DOM-интерфейс с WebGL-картой без дополнительного фреймворка.
Помимо popups и markers, HTML используется для построения полноценных overlay-интерфейсов: фильтров, панелей, легенд.
const panel = document.createElement('div');
panel.className = 'map-panel';
panel.innerHTML = `
<h2>Фильтры</h2>
<label>
<input type="checkbox" id="filter-cafes">
Кафе
</label>
`;
document.body.appendChild(panel);
Такой интерфейс не привязан к координатам карты и существует как независимый слой поверх canvas.
HTML в Mapbox GL JS часто обновляется в ответ на события данных или взаимодействия пользователя.
map.on('click', 'points-layer', (e) => {
const feature = e.features[0];
popup.setHTML(`
<div>
<h3>${feature.properties.name}</h3>
<p>${feature.properties.description}</p>
</div>
`);
popup.setLngLat(feature.geometry.coordinates).addTo(map);
});
В этом случае HTML становится частью реактивного интерфейса, зависящего от геоданных.
При усложнении интерфейса прямое формирование строк становится неудобным. Используются шаблонизаторы или функции генерации DOM.
function createPopupContent(data) {
const wrapper = document.createElement('div');
const title = document.createElement('h3');
title.textContent = data.name;
const desc = document.createElement('p');
desc.textContent = data.description;
wrapper.appendChild(title);
wrapper.appendChild(desc);
return wrapper;
}
popup.setDOMContent(createPopupContent(feature.properties));
Этот подход улучшает читаемость и снижает риск XSS.
DOM-элементы в Mapbox GL JS живут поверх WebGL-контекста, что накладывает ограничения:
Оптимизации:
HTML-элементы в Mapbox GL JS позиционируются через проекцию географических координат в экранные.
const point = map.project([71.4304, 51.1284]);
marker.style.transform = `
translate(${point.x}px, ${point.y}px)
`;
Хотя библиотека делает это автоматически, понимание механизма важно при создании кастомных overlay-систем.
При использовании React, Vue или Svelte HTML-контент часто переносится в компонентную модель. В этом случае Mapbox GL JS выступает как низкоуровневый слой отображения.
const popupNode = document.createElement('div');
root.render(<PopupComponent data={feature} />, popupNode);
popup.setDOMContent(popupNode);
Такой подход позволяет отделить географическую логику от UI-логики.
HTML-контент не интегрирован в WebGL-рендер напрямую, что накладывает архитектурные ограничения:
Это означает, что HTML используется исключительно как overlay-слой, а не как часть графического пайплайна карты.
HTML-элементы часто реагируют на события карты:
map.on('zoom', () => {
document.getElementById('zoom-level').textContent =
map.getZoom().toFixed(2);
});
Таким образом DOM становится панелью мониторинга состояния карты, синхронизированной в реальном времени с WebGL-рендером Mapbox GL JS.