## Триггеры (Trigger) и передовые методы управления: Сердце [[HTMX]] {#sec-f4f92dc347e1} В предыдущих статьях мы рассмотрели основные способы отправки запросов на сервер с помощью [[HTMX]]. Мы убедились, что, используя такие атрибуты, как `hx-get`, `hx-post`, `hx-put`, `hx-delete`, можно реализовать множество Ajax-операций без использования JavaScript-функции `fetch()`. Однако при работе с HTMX часто возникает желание контролировать **не столько сам факт отправки запроса, сколько момент его отправки**. Именно здесь на сцену выходит `hx-trigger`. Если `hx-get` или `hx-post` определяют, **"что делать"**, то `hx-trigger` определяет, **"когда это делать"**. Без `hx-trigger` HTMX может показаться простым Ajax-инструментом, отправляющим запросы только по клику на кнопку. Но именно с использованием `hx-trigger` HTMX начинает приносить настоящее удовлетворение. Например, без единой строчки JavaScript-кода становятся возможными следующие вещи: - Отправка поискового запроса только после прекращения ввода - Предотвращение повторных кликов за короткий промежуток времени - Автоматическое обновление с заданным интервалом - Загрузка элементов только при их появлении на экране - Отправка запросов только при определенных условиях - Настройка приоритетов для предотвращения конфликтов между различными запросами Изначально я думал: "Зачем использовать [[HTMX]], если то же самое можно сделать в несколько строк с помощью fetch?" — и воспринимал его как простой Ajax-инструмент. Но мое отношение полностью изменилось после того, как я оценил мощные возможности `hx-trigger`. В этой статье мы подробно рассмотрим **триггеры и передовые методы управления**, которые по праву считаются истинным сердцем HTMX.  --- ## [[HTMX]] — это не просто инструмент для кнопок {#sec-31cdd005939f} Когда впервые знакомишься с HTMX, обычно начинаешь с таких примеров: ```html
``` На первый взгляд HTMX может показаться просто "инструментом для отправки Ajax-запросов по нажатию кнопки". Безусловно, это уже очень удобно. Но это лишь малая часть возможностей HTMX. Что действительно важно, так это возможность **декларативно определять в HTML не сам запрос, а время и условия его возникновения**. Например, - Отправка запроса на сервер при каждом нажатии клавиши в поисковой строке — это худший UX. Намного естественнее было бы отправлять запрос только через 500 мс после прекращения ввода. - Или, возможно, вы захотите, чтобы кнопка срабатывала не при простом клике, а **только при клике с зажатой клавишей Ctrl**. - Или вы можете захотеть загружать данные только тогда, когда определенный элемент появляется на экране при прокрутке. Если каждый раз писать JavaScript для таких случаев, код быстро разрастется. HTMX же позволяет выразить большую часть такого управления **комбинациями атрибутов без единой строчки JavaScript**. Это действительно поразительно. **Шок, который испытывает C++ разработчик, впервые увидев код на Python и воскликнув: "Что? Здесь даже типы не объявляются?!", возможно, схож с моим шоком, когда я впервые увидел `hx-trigger` в HTMX**. --- ## Базовые триггеры: click, change, submit {#sec-a730147be101} Прежде всего, важно знать, что [[HTMX]] изначально разработан для гармоничной работы с базовым поведением HTML-элементов. Например, кнопки по умолчанию связаны с событием `click`, формы — с `submit`, а элементы ввода — с `change` в зависимости от ситуации. ```html ``` Эта кнопка отправит запрос по клику, даже если явно не указать `hx-trigger="click"`. То же самое касается и форм. ```html ``` В этом случае запрос будет отправлен при отправке формы. Иными словами, в самых базовых сценариях HTMX уже ведет себя достаточно 'умно'. Но нас интересует то, что начинается дальше. **Выйти за рамки значений по умолчанию и явно определить желаемые моменты и условия.** Именно этого мы и хотим добиться. --- ## Часто используемые стандартные события DOM против специальных триггеров [[HTMX]] в `hx-trigger` {#sec-8354ed76dc75} Прежде всего, необходимо ознакомиться со следующей таблицей. Главное — это то, что **стандартные события DOM, предоставляемые браузером, конечно же, доступны для использования + существуют несколько специальных триггеров HTMX**. | Категория | Значение | Значение / Описание | Типичные сценарии использования | Пример | | ---------- | ----------------- | ---------------------------- | ----------------------------------- | ---------------------------------------------------------------------- | | Стандартное событие | `click` | Клик по элементу | Кнопки, ссылки, выполнение действий | `hx-trigger="click"` | | Стандартное событие | `input` | Запрос при каждом изменении значения ввода | Поиск в реальном времени, автозаполнение | `hx-trigger="input changed delay:500ms"` | | Стандартное событие | `change` | Запрос при подтвержденном изменении значения | `select`, `checkbox`, применение ввода после потери фокуса | `hx-trigger="change"` | | Стандартное событие | `submit` | Запрос при отправке формы | Отправка формы | `hx-trigger="submit"` | | Стандартное событие | `keyup` | Запрос при отпускании клавиши | Поиск по вводу с клавиатуры, быстрая реакция | `hx-trigger="keyup delay:500ms"` | | Стандартное событие | `keydown` | Запрос в момент нажатия клавиши | Горячие клавиши, взаимодействие с клавиатурой | `hx-trigger="keydown[from:body]"` | | Стандартное событие | `mouseup` | Запрос при отпускании кнопки мыши | Реакция после перетаскивания/выбора | `hx-trigger="mouseup"` | | Специальный htmx | `load` | Запрос сразу после загрузки элемента | Отложенная загрузка, заполнение начальных данных | `hx-trigger="load"` | | Специальный htmx | `revealed` | Запрос при появлении элемента на экране | Бесконечная прокрутка, lazy loading | `hx-trigger="revealed"` | | Специальный htmx | `intersect` | Запрос при пересечении элемента с областью просмотра | Более точная lazy loading, загрузка на основе прокрутки | `hx-trigger="intersect once"` | | Специальный htmx синтаксис | `every 5s` | Запрос через определенный интервал | Опрос, обновление состояния | `hx-trigger="every 5s"` | | Пользовательское событие | `my-custom-event` | Запрос по пользовательскому событию | Заголовки сервера, интеграция JS, слабосвязанная архитектура событий | `hx-trigger="itemSaved from:body"` | | Модификатор | `delay:500ms` | Запрос только при отсутствии дополнительных событий в течение указанного времени | Debouncing, оптимизация поиска в реальном времени | `hx-trigger="keyup delay:500ms"` | | Модификатор | `throttle:1s` | Ограничение повторных запросов за короткий промежуток времени | Предотвращение повторных кликов, подавление избыточных запросов | `hx-trigger="click throttle:1s"` | | Модификатор | `once` | Запрос только один раз | Первая загрузка, одноразовые события | `hx-trigger="intersect once"` | | Модификатор | `changed` | Запрос только при фактическом изменении значения | Оптимизация полей ввода, предотвращение ненужных запросов | `hx-trigger="input changed delay:500ms"` | | Модификатор | `from:body` | Указание другого элемента для отслеживания событий | Прием глобальных событий, обработка пользовательских событий | `hx-trigger="itemSaved from:body"` | | Модификатор | `[condition]` | Запрос только при выполнении условия | Комбинации вспомогательных клавиш, условие длины ввода | `hx-trigger="click[ctrlKey]"` / `hx-trigger="keyup[value.length > 1]"` | | Модификатор | `consume` | Поглощение события, чтобы оно не передавалось родительским элементам | Предотвращение конфликтов вложенных htmx-запросов | `hx-trigger="click consume"` | | Модификатор | `queue:first` | При постановке нового события в очередь сохраняется только первое | Сохранение только первого запроса при последовательном вводе | `hx-trigger="input queue:first"` | | Модификатор | `queue:last` | При постановке нового события в очередь сохраняется только последнее | Поисковая строка, автозаполнение | `hx-trigger="input queue:last"` | | Модификатор | `queue:all` | Все произошедшие события сохраняются в очереди | Когда необходимо последовательно обработать все события | `hx-trigger="input queue:all"` | | Модификатор | `queue:none` | Игнорировать новые события, если запрос уже выполняется | Полная блокировка дублирующихся запросов | `hx-trigger="click queue:none"` | Значение `hx-trigger` обычно начинается с **события (event)**, а затем, при необходимости, добавляются **фильтры (filter)** и **модификаторы (modifier)**. То есть, читать следует в порядке: **“что произошло (event), при каком условии (filter), как обработать (modifier)”**. Обычно это выглядит как `event[filter] modifier modifier`. ```html ``` - `keyup` → Клавиша отпущена - `[value.length > 1]` → Только если длина введенного значения больше 1 символа - `changed delay:500ms` → Запрос только если значение изменилось и в течение 0.5 секунды не было дополнительного ввода ## Несколько полезных примеров {#sec-d893c37bc6f7} Хотя мы уже 정리ровали информацию в таблице, было бы жаль на этом останавливаться, поэтому я хотел бы показать несколько своих любимых примеров триггеров. ### Отправка запроса только после прекращения ввода: `delay` {#sec-cd31c7256394} При создании функций автозаполнения поисковой строки или фильтрации в реальном времени, отправка запроса при каждом нажатии клавиши пользователем создает нагрузку на сервер и делает пользовательский опыт несколько нетерпеливым. В таких случаях использование `delay` делает процесс намного плавнее. ```html ``` Этот код не отправляет запрос сразу при каждом нажатии клавиши пользователем. Вместо этого он отправляет запрос **через 500 мс после прекращения ввода**. Это, по сути, **debounce**. Для реализации на JavaScript потребовался бы код, который устанавливает таймер, отменяет предыдущий таймер и устанавливает его заново. Но в [[HTMX]] это решается просто с помощью атрибута. Для поисковых систем, автоподсказок и интерфейсов фильтрации эта функция является практически базовой. --- ### Не отправлять слишком часто: `throttle` {#sec-c84ae134ee88} Если `delay` создает ощущение "подождать немного после прекращения ввода и отправить", то `throttle` ближе к "ограничению слишком частых отправок за короткий промежуток времени". ```html ``` В этом случае, даже если пользователь очень быстро нажмет кнопку несколько раз, можно контролировать, чтобы избыточные запросы не отправлялись последовательно в течение 1 секунды. Это весьма полезно в следующих ситуациях: - Предотвращение повторных кликов - Блокировка слишком быстрых повторяющихся запросов - Снижение нагрузки на сервер - Предотвращение случайного выполнения одного и того же действия несколько раз Особенно стоит рассмотреть это для кнопок типа "Нравится", "Сохранить", "Обновить", "Синхронизировать". --- ### Автоматическая отправка запросов через определенные интервалы: `every` {#sec-0195e4e0d263} При использовании [[HTMX]] одной из удивительно привлекательных функций является `every`. Если вы хотите обновлять определенную область с сервера через регулярные интервалы, вам не нужно писать отдельную логику опроса на JavaScript. ```html