События маркеров

Маркеры в Leaflet являются интерактивными слоями, которые тесно связаны с системой событий карты и DOM-событий. Каждый маркер представляет собой объект, способный реагировать на пользовательские действия, изменять своё состояние и передавать информацию о происходящих взаимодействиях через единый механизм событий.

Маркер в Leaflet наследует поведение от Evented, что обеспечивает стандартный набор методов для работы с событиями:

  • on(type, handler, context?) — подписка на событие
  • off(type, handler?) — удаление обработчика
  • once(type, handler) — одноразовая подписка
  • fire(type, data?) — вызов события вручную

Каждый маркер может одновременно обрабатывать несколько типов событий, а также иметь несколько обработчиков на одно событие.

Основные пользовательские события маркеров

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

click

Срабатывает при клике по маркеру. Является базовым событием для интерактивных карт.

marker.on('click', function (e) {
    console.log(e.latlng);
});

Объект события содержит координаты точки клика:

  • e.latlng — географические координаты
  • e.target — сам маркер
  • e.type — строка события

dblclick

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

marker.on('dblclick', function (e) {
    map.setView(e.latlng, 14);
});

mouseover и mouseout

Используются для визуальной обратной связи при наведении курсора.

marker.on('mouseover', function () {
    this.setOpacity(0.7);
});

marker.on('mouseout', function () {
    this.setOpacity(1);
});

Эти события важны для UI-поведения: подсветка, отображение подсказок, изменение иконки.

contextmenu

Срабатывает при правом клике. Позволяет реализовать контекстные меню.

marker.on('contextmenu', function (e) {
    console.log('Контекстное меню', e.latlng);
});

События перетаскивания маркера

При включении опции draggable: true маркер становится перемещаемым и начинает генерировать дополнительные события.

const marker = L.marker([51.5, -0.09], {
    draggable: true
}).addTo(map);

dragstart

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

marker.on('dragstart', function () {
    console.log('Перетаскивание началось');
});

drag

Срабатывает во время движения маркера.

marker.on('drag', function (e) {
    console.log(e.target.getLatLng());
});

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

dragend

Срабатывает после завершения перемещения.

marker.on('dragend', function (e) {
    const position = e.target.getLatLng();
    console.log('Новая позиция:', position);
});

События изменения позиции

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

move

Срабатывает при любом изменении положения маркера.

marker.on('move', function (e) {
    console.log('Маркер перемещён');
});

moveend

Срабатывает после завершения перемещения (аналогично dragend, но включает программные изменения).

marker.on('moveend', function () {
    console.log('Перемещение завершено');
});

Взаимодействие с попапами и тултипами

Маркер может автоматически управлять всплывающими элементами, которые также генерируют события.

popupopen

Срабатывает при открытии всплывающего окна.

marker.on('popupopen', function () {
    console.log('Popup открыт');
});

popupclose

Срабатывает при закрытии всплывающего окна.

marker.on('popupclose', function () {
    console.log('Popup закрыт');
});

tooltipopen и tooltipclose

Аналогичные события для подсказок:

marker.on('tooltipopen', function () {
    console.log('Tooltip открыт');
});

Структура объекта события

Все события маркера имеют единый формат объекта события, расширенный в зависимости от типа действия.

Общие поля:

  • type — тип события
  • target — объект маркера
  • latlng — географические координаты (для событий мыши)
  • layerPoint — координаты относительно слоя
  • containerPoint — координаты относительно контейнера карты

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

Контекст исполнения обработчиков

В Leaflet значение this внутри обработчика события обычно указывает на объект, к которому привязано событие. Для маркера это сам экземпляр L.Marker.

marker.on('click', function () {
    this.openPopup();
});

Также можно явно задать контекст:

marker.on('click', function () {
    console.log(this.id);
}, customContext);

Удаление обработчиков

Корректное управление подписками критично для предотвращения утечек памяти.

Удаление конкретного обработчика

function onClick(e) {
    console.log(e.latlng);
}

marker.on('click', onClick);
marker.off('click', onClick);

Удаление всех обработчиков события

marker.off('click');

Полная очистка событий

marker.off();

Одноразовые события

Метод once позволяет обработать событие только один раз.

marker.once('click', function () {
    console.log('Сработает только один раз');
});

После первого вызова обработчик автоматически удаляется.

Всплытие событий и взаимодействие с картой

События маркера могут взаимодействовать с событиями карты. Например, клик по маркеру может также вызывать клик по карте, если не остановить распространение события.

marker.on('click', function (e) {
    L.DomEvent.stopPropagation(e);
});

Механизм распространения событий позволяет строить сложные иерархии взаимодействий между слоями.

Пользовательские события маркеров

Маркер может генерировать собственные события через fire.

marker.fire('customEvent', {
    data: 'value'
});

И обработка:

marker.on('customEvent', function (e) {
    console.log(e.data);
});

Это используется для интеграции маркеров с внешними системами состояния.

Особенности производительности

События mousemove, drag и move могут генерироваться с высокой частотой. При большом количестве маркеров это становится критическим фактором производительности.

Типичные подходы оптимизации:

  • минимизация логики внутри частых событий
  • использование throttle или debounce
  • отключение ненужных обработчиков через off
  • группировка маркеров (например, через кластеризацию)

Взаимодействие с иконками и DOM

Маркер в Leaflet представлен DOM-элементом, поэтому часть событий фактически является обёрткой над DOM-событиями. Это объясняет поведение hover, click и contextmenu.

При кастомных иконках:

const icon = L.icon({
    iconUrl: 'icon.png',
    iconSize: [32, 32]
});

const marker = L.marker([51.5, -0.09], { icon }).addTo(map);

События остаются теми же, независимо от визуального представления.

Согласованность событий при изменении состояния

При динамическом изменении состояния маркера (перемещение, изменение иконки, привязка popups) Leaflet гарантирует сохранение корректного порядка событий:

  1. изменение состояния
  2. генерация move / update
  3. завершение операции (moveend и аналогичные)

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