Обработчики событий

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

В основе системы лежит объектная модель событий, позволяющая подписываться на определённые действия и выполнять произвольный код при их возникновении.

Практически все основные объекты Leaflet поддерживают механизм событий:

  • карта (L.Map);
  • маркеры (L.Marker);
  • всплывающие окна (L.Popup);
  • векторные объекты;
  • слои;
  • группы слоёв;
  • пользовательские компоненты.

Подписка на события

Для регистрации обработчика используется метод on().

map.on('click', function(event) {
    console.log(event);
});

После каждого щелчка по карте функция будет вызываться автоматически.

Сокращённая запись через стрелочную функцию:

map.on('click', (event) => {
    console.log(event.latlng);
});

Объект события

Каждый обработчик получает объект события, содержащий информацию о произошедшем действии.

Пример:

map.on('click', function(event) {
    console.log(event.latlng);
});

Результат:

LatLng(55.75, 37.61)

Наиболее часто используемые свойства:

Свойство Описание
latlng Географические координаты
layerPoint Координаты относительно слоя
containerPoint Координаты относительно контейнера карты
target Объект, вызвавший событие
sourceTarget Исходный источник события
type Тип события

Получение типа события:

map.on('click', function(event) {
    console.log(event.type);
});

Вывод:

click

Несколько событий одновременно

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

map.on('click dblclick', function(event) {
    console.log(event.type);
});

При клике:

click

При двойном клике:

dblclick

Использование объекта конфигурации

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

map.on({
    click: function() {
        console.log('Клик');
    },

    zoomend: function() {
        console.log('Масштаб изменён');
    },

    moveend: function() {
        console.log('Карта перемещена');
    }
});

Такой подход делает код компактнее и удобнее при большом количестве событий.


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

Для удаления используется метод off().

Регистрация:

function handleClick(event) {
    console.log(event.latlng);
}

map.on('click', handleClick);

Удаление:

map.off('click', handleClick);

После удаления функция больше не будет вызываться.


Однократные обработчики

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

map.once('click', function(event) {
    console.log('Первый клик');
});

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

Полезно для:

  • первоначальной инициализации;
  • выбора точки на карте;
  • разового подтверждения действия.

События карты

click

Возникает при щелчке мышью по карте.

map.on('click', function(event) {
    console.log(event.latlng);
});

dblclick

Срабатывает при двойном клике.

map.on('dblclick', function() {
    console.log('Двойной клик');
});

contextmenu

Возникает при нажатии правой кнопки мыши.

map.on('contextmenu', function(event) {
    console.log(event.latlng);
});

Часто используется для создания собственных контекстных меню.


mousedown

Нажатие кнопки мыши.

map.on('mousedown', function() {
    console.log('Кнопка нажата');
});

mouseup

Отпускание кнопки мыши.

map.on('mouseup', function() {
    console.log('Кнопка отпущена');
});

mousemove

Перемещение указателя по карте.

map.on('mousemove', function(event) {
    console.log(event.latlng);
});

Применяется для:

  • отображения координат курсора;
  • интерактивного рисования;
  • измерительных инструментов.

mouseover и mouseout

Наведение и уход курсора.

map.on('mouseover', function() {
    console.log('Курсор над картой');
});

map.on('mouseout', function() {
    console.log('Курсор покинул карту');
});

События масштабирования

zoomstart

Начало изменения масштаба.

map.on('zoomstart', function() {
    console.log('Масштабирование началось');
});

zoom

Вызывается во время масштабирования.

map.on('zoom', function() {
    console.log(map.getZoom());
});

Событие может срабатывать многократно.


zoomend

Завершение масштабирования.

map.on('zoomend', function() {
    console.log('Текущий масштаб:', map.getZoom());
});

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


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

movestart

Начало перемещения карты.

map.on('movestart', function() {
    console.log('Перемещение началось');
});

move

Происходит во время движения.

map.on('move', function() {
    console.log('Карта движется');
});

Из-за большого количества вызовов рекомендуется использовать осторожно.


moveend

Возникает после завершения движения.

map.on('moveend', function() {

    const center = map.getCenter();

    console.log(center);
});

Особенно полезно для динамической загрузки данных.


Динамическая загрузка объектов после перемещения

Типичный сценарий:

map.on('moveend', function() {

    const bounds = map.getBounds();

    loadData(bounds);
});

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

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


События загрузки карты

load

Возникает после полной инициализации карты.

map.on('load', function() {
    console.log('Карта готова');
});

unload

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

map.on('unload', function() {
    console.log('Карта удаляется');
});

Полезно для освобождения ресурсов.


События слоёв

При добавлении и удалении слоёв генерируются специальные события.

layeradd

map.on('layeradd', function(event) {
    console.log(event.layer);
});

layerremove

map.on('layerremove', function(event) {
    console.log(event.layer);
});

Пример:

const marker = L.marker([55.75, 37.61]);

map.addLayer(marker);

После добавления будет вызван обработчик layeradd.


Обработчики событий маркеров

Маркеры поддерживают практически все события мыши.

const marker = L.marker([55.75, 37.61]).addTo(map);

Регистрация:

marker.on('click', function() {
    console.log('Маркер выбран');
});

Получение координат маркера

marker.on('click', function(event) {

    console.log(
        event.target.getLatLng()
    );

});

Изменение иконки при наведении

marker.on('mouseover', function() {

    this.setIcon(activeIcon);

});

marker.on('mouseout', function() {

    this.setIcon(defaultIcon);

});

Подобный подход часто применяется для интерактивных карт объектов недвижимости, магазинов или туристических точек.


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

Для работы необходимо включить режим перетаскивания.

const marker = L.marker(
    [55.75, 37.61],
    {
        draggable: true
    }
).addTo(map);

dragstart

Начало перетаскивания.

marker.on('dragstart', function() {
    console.log('Начало перемещения');
});

drag

Во время перемещения.

marker.on('drag', function(event) {

    console.log(
        event.target.getLatLng()
    );

});

dragend

Завершение перетаскивания.

marker.on('dragend', function(event) {

    const position =
        event.target.getLatLng();

    console.log(position);

});

Частое применение:

  • выбор адреса;
  • установка точки доставки;
  • указание местоположения объекта.

События всплывающих окон

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

marker.bindPopup('Информация');

popupopen

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

popupclose

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

События всплывающих подсказок

Для подсказок (Tooltip) доступны аналогичные обработчики.

marker.bindTooltip('Подсказка');

Открытие:

marker.on('tooltipopen', function() {
    console.log('Подсказка открыта');
});

Закрытие:

marker.on('tooltipclose', function() {
    console.log('Подсказка закрыта');
});

События векторных объектов

Все геометрические примитивы поддерживают обработчики событий.

Создание полигона:

const polygon = L.polygon([
    [55.76, 37.60],
    [55.74, 37.64],
    [55.72, 37.58]
]).addTo(map);

Обработка клика:

polygon.on('click', function() {
    console.log('Полигон выбран');
});

Изменение стиля по событию

Частая практика при работе с геометрией.

polygon.on('mouseover', function() {

    this.setStyle({
        weight: 5
    });

});

polygon.on('mouseout', function() {

    this.setStyle({
        weight: 2
    });

});

Так создаётся визуальная реакция на действия пользователя.


Передача контекста обработчику

Третий параметр метода on() задаёт контекст выполнения.

const app = {

    name: 'Map Application',

    handleClick() {
        console.log(this.name);
    }

};

map.on(
    'click',
    app.handleClick,
    app
);

Вывод:

Map Application

Без передачи контекста значение this было бы другим.


Генерация собственных событий

Leaflet позволяет создавать пользовательские события.

Отправка:

map.fire('dataLoaded');

Подписка:

map.on('dataLoaded', function() {
    console.log('Данные загружены');
});

Передача данных в пользовательское событие

map.fire('userSelected', {
    id: 25,
    name: 'Москва'
});

Получение:

map.on('userSelected', function(event) {

    console.log(event.id);
    console.log(event.name);

});

Вывод:

25
Москва

Проверка наличия обработчиков

Метод listens() позволяет определить, подписан ли объект на событие.

if (map.listens('click')) {
    console.log('Есть обработчики');
}

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


Практический пример выбора точки на карте

let marker;

map.on('click', function(event) {

    if (marker) {
        map.removeLayer(marker);
    }

    marker = L.marker(
        event.latlng
    ).addTo(map);

});

После каждого щелчка старый маркер удаляется, а новый устанавливается в выбранную точку.


Практический пример отображения координат курсора

map.on('mousemove', function(event) {

    document.getElementById('lat')
        .textContent =
        event.latlng.lat.toFixed(6);

    document.getElementById('lng')
        .textContent =
        event.latlng.lng.toFixed(6);

});

Так реализуются панели текущих координат на картографических сервисах.


Практический пример загрузки данных по области просмотра

map.on('moveend', function() {

    const bounds =
        map.getBounds();

    fetch('/api/objects', {
        method: 'POST',
        body: JSON.stringify({
            north: bounds.getNorth(),
            south: bounds.getSouth(),
            east: bounds.getEast(),
            west: bounds.getWest()
        })
    });

});

Сервер получает только текущую видимую область карты, что значительно снижает объём передаваемых данных и повышает производительность приложения.


Рекомендации по работе с событиями

Использование moveend вместо move для загрузки данных

Событие move может вызываться десятки раз в секунду. Для запросов к серверу обычно предпочтительнее moveend.

Удаление неиспользуемых обработчиков

При динамическом создании компонентов рекомендуется своевременно вызывать off(), чтобы избежать утечек памяти.

Минимизация тяжёлых операций

Обработчики событий должны выполняться быстро. Длительные вычисления во время mousemove, drag или zoom могут привести к заметным задержкам интерфейса.

Разделение логики

Сложные обработчики желательно выносить в отдельные функции:

function updateData() {
    console.log('Обновление данных');
}

map.on('moveend', updateData);

Так код становится проще поддерживать и тестировать.

Использование пользовательских событий

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