Модель событий в браузерных интерфейсах основана на иерархии объектов, где одно и то же действие пользователя может последовательно обрабатываться на нескольких уровнях. В контексте картографических приложений это особенно заметно: взаимодействие с геообъектами (маркерами, полигонами, слоем карты) часто приводит к каскаду событий, которые могут быть перехвачены как на уровне конкретного объекта, так и на уровне самой карты.
Всплытие событий (event bubbling) представляет собой механизм, при котором событие, возникшее на дочернем элементе, последовательно поднимается вверх по цепочке объектов, пока не достигнет корневого обработчика или не будет остановлено явно.
Событийная модель в API Google Maps построена поверх стандартной
модели DOM, но имеет собственную реализацию диспетчеризации событий.
Основной механизм основан на классе google.maps.MVCObject,
от которого наследуются ключевые сущности API: Map,
Marker, Polyline, Polygon,
InfoWindow и другие.
Каждый объект, основанный на MVCObject, поддерживает
регистрацию слушателей через:
google.maps.event.addListener(instance, eventName, handler);
или более современный подход:
instance.addListener(eventName, handler);
События в Google Maps API не всегда строго следуют DOM-модели bubbling/capturing, однако концептуально многие из них реализуют аналогичную иерархическую передачу.
DOM-события, например клики по кнопке, проходят фазу:
В Google Maps JavaScript API модель проще и ближе к dispatch-based системе: событие генерируется объектом и может быть обработано слушателями этого объекта и иногда глобальными слушателями карты.
Пример DOM:
document.body.addEventListener("click", () => {
console.log("body click");
});
Пример Maps API:
const marker = new google.maps.Marker({
position: { lat: 50, lng: 30 },
map: map
});
marker.addListener("click", () => {
console.log("marker click");
});
Здесь событие «click» возникает не в DOM, а внутри слоя карты и обрабатывается системой событий API.
Всплытие событий в картографическом интерфейсе проявляется как передача события от геометрического объекта к карте. Например:
Каждое из этих событий может быть обработано:
При клике на маркер происходит генерация события click.
Оно не распространяется в DOM, но логически может быть интерпретировано
как всплывающее внутри системы карты.
const marker = new google.maps.Marker({
position: { lat: 55.75, lng: 37.61 },
map
});
marker.addListener("click", (event) => {
console.log("Клик по маркеру");
});
Если зарегистрировать обработчик на карте:
map.addListener("click", (event) => {
console.log("Клик по карте");
});
При клике по маркеру обработчик карты может не сработать, если событие было перехвачено маркером. Это поведение имитирует остановку всплытия.
В DOM используется:
event.stopPropagation();
В Google Maps API аналогичное поведение реализуется через:
google.maps.event.addListener(marker, "click", (event) => {
event.stop();
});
Метод stop() предотвращает дальнейшую обработку события
другими слушателями, включая глобальные обработчики карты.
Событийная модель можно представить как дерево:
Map
Layer
При возникновении события на уровне Marker оно может быть обработано:
Но поведение зависит от типа события. Например, события
drag, click, rightclick имеют
разные правила распространения.
map.addListener("click", (e) => {
console.log("MAP CLICK");
});
const marker = new google.maps.Marker({
position: { lat: 51.5, lng: -0.12 },
map
});
marker.addListener("click", (e) => {
console.log("MARKER CLICK");
});
При клике по маркеру:
MARKER CLICKMAP CLICK может не выполнитьсяТаким образом, маркер перехватывает событие до того, как оно достигнет карты.
InfoWindow добавляет дополнительный уровень обработки
событий. При открытии окна и взаимодействии с его содержимым события
снова переходят в DOM-модель.
const infoWindow = new google.maps.InfoWindow({
content: "<div>Текст</div>"
});
marker.addListener("click", () => {
infoWindow.open(map, marker);
});
Здесь возникает смешанная модель:
Особенность архитектуры заключается в том, что карта рендерится внутри DOM-элемента, но логика взаимодействия абстрагирована.
Например:
map.getDiv().addEventListener("click", () => {
console.log("DOM click on map container");
});
Этот обработчик сработает независимо от событий Google Maps API, если они не остановили всплытие DOM-события.
Однако важно различать:
Они существуют параллельно.
Полигональные объекты ведут себя аналогично маркерам, но с дополнительной сложностью: у них есть геометрическая область.
const polygon = new google.maps.Polygon({
paths: [
{ lat: 52, lng: 30 },
{ lat: 53, lng: 30 },
{ lat: 52.5, lng: 31 }
],
map
});
polygon.addListener("click", (e) => {
console.log("POLYGON CLICK");
});
При клике внутри полигона:
Всплытие позволяет реализовать делегирование: один обработчик на карте может обрабатывать события множества объектов.
map.addListener("click", (event) => {
if (event.placeId) {
console.log("Клик по месту:", event.placeId);
}
});
Такой подход снижает количество обработчиков и упрощает управление логикой.
При сложных интерфейсах возникает конфликт между слоями:
Для управления используется:
stop() для блокировки распространенияevent.targetПри создании OverlayView разработчик получает полный
контроль над DOM-слоем карты:
class CustomOverlay extends google.maps.OverlayView {
onAdd() {
const div = document.createElement("div");
div.textContent = "Overlay";
this.getPanes().overlayLayer.appendChild(div);
}
draw() {}
}
События внутри такого слоя снова становятся DOM-событиями и начинают всплывать по стандартным правилам браузера, а не Google Maps API.
Архитектура Google Maps JavaScript API сочетает три уровня:
Всплытие становится не единым механизмом, а совокупностью взаимодействующих систем.
Механизм всплытия позволяет:
При этом неправильное управление остановкой событий приводит к конфликтам между слоями и неожиданному поведению интерфейса, когда одно действие пользователя вызывает несколько независимых реакций на разных уровнях карты.