Что такое htmx
Введение
HTMX — это небольшая (14k min. gzipped), свободная от зависимостей библиотека JavaScript, позволяющая создавать передовые пользовательские интерфейсы с лёгкостью и силой гипертекста (разметки). Она предоставляет доступ к AJAX, CSS Transitions, WebSockets и Server Sent Events напрямую в HTML с помощью атрибутов. Это стало переломным моментом для разработчиков, поскольку позволяет им добиться интерактивности (которую даёт JavaScript), но непосредственно из HTML разметки.
Установка HTMX
Вы можете установить HTMX тремя способами:
Установка через CDN
Вы можете загрузить HTMX на свой сайт/приложение, включив CDN в заголовок HTML-файла.
<script
src="https://unpkg.com/htmx.org@1.9.6"
integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"
crossorigin="anonymous"
></script>
Примечание: Хотя это возможно, но не рекомендуется использовать CDN в продакшене.
Загрузка копии на сайт или в приложение
Вы можете загрузить копию исходного файла HTMX на свой сайт/приложение.
Загрузите htmx.min.js
с сайта unpkg.com, поместите его в нужную директорию вашего проекта, а затем включите его в тег <script>
при необходимости:
<script src="/path/to/htmx.min.js"></script>
Установка HTMX через npm
Вы можете установить HTMX через npm с помощью следующей команды:
npm install htmx.org
После установки вам нужно будет использовать соответствующий инструментарий для использования node_modules/htmx.org/dist/htmx.js
или node_modules/htmx.org/dist/htmx.min.js
. Например, вы можете использовать HTMX в пакете с определёнными расширениями и специфическим для проекта кодом.
Установка HTMX в Webpack
Если вы используете Webpack для управления JavaScript, во-первых, установите htmx через менеджер пакетов(npm, yarn, pnpm), а затем импортируйте в index.js
import "htmx.org";
Если вы хотите использовать глобальную переменную htmx
, вы можете ввести её в область видимости window
следующим образом:
Создайте уникальный JS-файл.
Импортируйте этот файл в файл
index.js
.import "path/to/my_custom.js";
Затем добавьте в файл приведённый ниже код
window.htmx = require("htmx.org");
Наконец, пересоберите пакет приложения (чтобы отразить изменения).
Ajax-запросы с помощью HTMX
HTMX предоставляет атрибуты, позволяющие выполнять AJAX-запросы непосредственно из HTML:
HTMX атрибут | Описание атрибута |
---|---|
hx-post | выполняет POST запрос по заданному url |
hx-get | выполняет GET запрос по заданному url |
hx-put | выполняет PUT запрос по заданному url |
hx-patch | выполняет PATCH запрос по заданному url |
hx-delete | выполняет DELETE запрос по заданному url |
<div hx-post="/messages">Put To Messages</div>
В приведённом выше коде атрибут hx-post
выполняет POST запрос к API по адресу /messages
.
Триггер запроса
В HTMX естественное событие элемента инициирует AJAX-запросы. Например, событие onchange
инициируют input
, select
и textarea
; событие onsubmit
инициирует form
; а событие onclick
запускает всё остальное.
Однако HTMX предлагает уникальный атрибут hx-trigger
для ситуаций, когда вы хотите изменить событие, инициирующее запрос:
<div hx-post="http://localhost:3000/submit" hx-trigger="mouseenter">
Submit
</div>
В приведённом выше коде только при наведении мыши пользователя на div
будет выполнен GET-запрос к указанному URL.
Модификаторы триггеров
Поведение триггера можно также изменить с помощью нескольких других модификаций. Например, используйте модификатор once
для триггера, если вы хотите, чтобы запрос выполнялся только один раз:
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
Submit
</div>
Другие модификаторы триггера, которые вы можете использовать:
changed
— отправка запроса только в том случае, если значение элемента изменилось.delay:<временной интервал>
— задаёт период задержки перед выполнением запроса. Отсчёт сбрасывается при повторном возникновении события.throttle:<временной интервал>
— задаёт время задержки перед выполнением запроса. В отличие отdelay
, если новое событие происходит до того, как будет достигнут лимит времени, событие удаляется, а запрос срабатывает по окончании временного интервала.from:<селектор CSS>
— прослушивает событие другого элемента.
Посмотреть другие модификаторы, поддерживаемые HTMX, можно здесь
Фильтры триггеров
Они применяются с помощью заключения выражения JavaScript в квадратные скобки []
после имени события. Если выражение равно true
, событие будет вызвано, в противном случае — нет.
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Click the force
</div>
В htmx есть несколько специальных событий, которые можно использовать с hx-trigger
:
load
— срабатывает один раз, когда элемент загружается в первый раз.revealed
— срабатывает, когда элемент впервые прокручивается в область просмотраintersect
— срабатывает, когда элемент впервые пересекает область просмотра. Позволяет использовать два дополнительных варианта:root:<selector>
— CSS-селектор корневого элемента для пересеченияthreshold:<float>
— число с плавающей точкой между0,0
и1,0
, указывающее величину пересечения, при котором должно срабатывать событие.
Если у вас более сложный вариант использования, вы также можете использовать пользовательские события для запуска запросов.
Опрос
Вы можете использовать синтаксис every
с временными рамками (в секундах) со свойством hx-trigger
, чтобы заставить элемент опрашивать определённый URL:
<div hx-get="/get-star-wars-data" hx-trigger="every 2s"></div>
Опрос загрузки
"Опрос загрузки" — это дополнительный метод опроса в HTMX. Он заключается в том, что элемент задаёт триггер загрузки и задержку, а затем заменяет себя ответом:
<div hx-get="/get-star-wars-data" hx-trigger="load delay:1s" hx-swap="starWarsDataHTML"></div>
Индикаторы запроса
Поскольку браузер не предоставляет обратной связи при выполнении AJAX-запроса, часто бывает полезно уведомить пользователя, что что-то происходит. Это можно сделать с помощью HTMX, используя класс htmx-indicator
.
Непрозрачность любого элемента с классом htmx-indicator
по умолчанию равна 0
. Это означает, что хотя элемент и присутствует в DOM, он невидим.
Класс htmx-request
добавляется к элементу, когда HTMX отправляет запрос. Когда дочерний элемент с классом htmx-indicator
подвергается воздействию класса htmx-request
, он переходит к непрозрачности 1
, отображая индикатор.
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
Используя атрибут hx-indicator
и селектор CSS, вы можете добавить класс htmx-request
альтернативному элементу, если захотите:
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click the force!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif" />
</div>
Цели
Вы можете загрузить ответ в элемент, отличный от того, который инициировал запрос, с помощью атрибута hx-target
, принимающего CSS-селектор (div
, class
и т. д.).
<input type="text" name="star-wars-input"
hx-get="/star-wars-characters"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
В приведённом выше примере атрибут hx-target
указывает идентификатор #search-result
и при успешном запросе выводит значения запроса в div
, содержащий этот идентификатор.
Замена
В HTMX можно заменить содержимое HTML, возвращаемое в DOM. innerHTML
целевого элемента автоматически заменяется содержимым. Это можно изменить, применив любое из следующих значений к атрибуту hx-swap
:
Имя | Описание |
---|---|
innerHTML | Содержимое помещается внутрь целевого элемента по умолчанию. |
outerHTML | Возвращаемое содержимое полностью заменяет целевой элемент. |
afterbegin | Содержимое добавляется перед первым дочерним элементом целевого элемента |
beforebegin | Добавляет содержимое родительского элемента цели перед целью. |
beforeend | Добавляет содержимое после последнего дочернего элемента цели. |
afterend | добавляет содержимое в родительский элемент цели после цели |
delete | Удаляет целевой элемент независимо от ответа |
none | Не добавляет содержимое ответа |
пример hx-swap
в действии
<div hx-get="/example" hx-swap="afterend">
Get Some HTML & Append It
</div>
В приведённом выше коде div
выполнит запрос и добавит полученное содержимое за div
.
Синхронизация
Когда вы выполняете запросы в двух элементах и хотите, чтобы один из них имел приоритет над другим или ждал, пока запрос другого элемента будет выполнен, в HTMX есть полезная функция hx-sync
.
Например, давайте представим сценарий гонки между отправкой формы и запросом на проверку определённого поля ввода:
<form hx-post="/submit-character">
<input id="title" name="title" type="text"
hx-post="/validate-character"
hx-trigger="change"
>
<button type="submit">Submit</button>
</form>
Когда ввод завершён и форма сразу отправлена, в /validate-character
и /submit-character
отправляются два одновременных запроса без использования hx-sync
.
Когда к input
применяется hx-sync="closest form:abort"
, форма будет следить за запросами, и запрос input
будет прерван, если запрос формы будет обнаружен или начнётся во время обработки запроса поля input
. Это автоматически решает ограничение синхронизации.
<form hx-post="/submit-character">
<input id="title" name="title" type="text"
hx-post="/validate-character"
hx-trigger="change"
hx-sync="closest form:abort"
>
<button type="submit">Submit</button>
</form>
Кроме того, htmx предлагает программный метод отмены запросов: Чтобы остановить все выполняющиеся запросы, вы можете отправить элементу событие htmx:abort
в функцию htmx.trigger
:
<button id="request-button" hx-post="/submit-character">
Submit Character
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Submission
</button>
CSS Transitions
HTMX позволяет выполнять CSS Transitions (CSS переходы) без использования JavaScript. Продемонстрируем это на примере:
<div id="div1">The Empire</div>
Затем мы выполняем ajax-запрос и меняем содержимое на приведённое ниже:
<div id="div1" class="red">
The Republic
</div>
Из приведённого выше кода видно, что к новому содержимому был добавлен класс red
. В этом сценарии мы можем создать CSS переход от предыдущего состояния (без класса red
) к текущему (с классом red
):
.red {
color: red;
transition: all ease-in 1s;
}
Замены вне группы
Атрибут hx-swap-oob
в ответе html может быть использован для замены содержимого ответа непосредственно в DOM с помощью атрибута id
:
<div id="message" hx-swap-oob="true">Dark Sidious</div>
Anakin Skywalker
Из приведённого выше кода, дополнительное содержимое будет подставлено в целевой элемент обычным способом, но div#message
будет подставлен прямо в соответствующий элемент DOM в этом ответе.
Параметры
Вы можете фильтровать аргументы, которые будут переданы при вызове AJAX, с помощью атрибута hx-params
.
Вот возможные значения этого атрибута:
*
— Включает все параметры по умолчаниюnone
— Не включает<список-параметров>
и не включает никаких параметров. Всё, кроме списка имён параметров<список-параметров>
, должен быть разделён запятыми. Предоставьте список имён всех параметров, разделённых запятыми.
<div hx-get="/get-starwars-characters" hx-params="*">
Luke Skywalker
</div>
В приведённом выше примере все параметры, которые содержал бы POST
, содержатся в этом div
, однако как это обычно бывает при GET
, они будут URL закодированы и включены в URL.
Подтверждение запросов
HTMX предоставляет атрибут hx-confirm
, позволяющий подтвердить действие с помощью простого JavaScript-диалога:
<button hx-delete="/delete-character" hx-confirm="Are you sure you wish to delete Obiwan Kenobi">
Delete Obiwan Kenobi
</button>
Веб-сокеты и SSE
На данный момент в HTMX есть экспериментальная поддержка WebSocket и Server Sent Event. Однако, если вы хотите установить соединение WebSocket, вы можете использовать атрибут hx-ws
<div hx-ws="connect:wss:/darth-chatroom">
<div id="chat_room">
...
</div>
<form hx-ws="send:submit">
<input name="chat_message">
</form>
</div>
Для отправки событий в браузеры с помощью SSE (Server sent Events) добавьте объявление connect <url>
к родительскому элементу и атрибут hx-sse
с URL.
После этого вы можете использовать атрибут hx-trigger="sse:<имя_события>"
для определения элементов, являющихся потомками этого элемента и активируемых событиями, посылаемыми сервером.
<body hx-sse="connect:/darth-events">
<div hx-trigger="sse:new_news" hx-get="/news"></div>
</body>
Поддержка истории браузера
С помощью атрибута hx-push-url
на элементе можно вывести URL-адрес запроса в навигационную панель браузера и добавить текущее состояние страницы в историю:
<a hx-get="/star-wars-characters" hx-push-url="true">
Blog
</a>
HTMX имитирует "возврат" к предыдущему состоянию, когда пользователь нажимает кнопку "назад", извлекая старое содержимое из хранилища и заменяя его на целевое. HTMX отправит ajax-вызов на указанный URL с заголовком HX-History-Restore-call
, установленным в true
, если местоположение не будет найдено в кэше, и будет ожидать возвращения HTML, необходимого для полной страницы. В качестве альтернативы он принудительно обновит страницу в браузере, если параметр htmx.config.refreshOnHistoryMiss
установлен в true
.
Валидация с HTMX
Поскольку HTMX работает с HTML5 Validation API, недействительный валидируемый ввод не позволит выполнить запрос формы. Это относится как к WebSocket, так и к AJAX-запросам.
События, связанные с валидацией в HTMX, включают:
htmx:validation:validate
— вызывается перед вызовом методаcheckValidity()
элементов. Может использоваться для добавления пользовательской логики валидации.htmx:validation:failed
— вызывается, когда функцияcheckValidity()
возвращаетfalse
, указывая на недопустимый ввод.htmx:validation:halted
— вызывается, когда запрос не выдаётся из-за ошибок валидации. Конкретные ошибки можно найти в объектеevent.detail.errors
.
<form hx-post="/create-characters">
<input _="on htmx:validation:validate
if my.value != 'foo'
call me.setCustomValidity('Please enter the value foo')
else
call me.setCustomValidity('')"
name="example"
>
</form>
Приведённый выше код является иллюстрацией ввода с использованием htmx:validation
. В коде используется hyperscript для проверки события, чтобы убедиться, что вводимое значение имеет значение foo
.
Анимация с HTMX
HTMX предназначен для добавления плавных анимаций и переходов на веб-страницы с помощью только CSS и HTML. Теперь вы можете использовать новый View Transitions API для создания анимации с HTMX. Базовый пример — анимация Fade Out On Swap. Если вам нужно затушить элемент, который будет удалён по завершении запроса, используйте класс htmx-swapping
с некоторыми CSS и увеличьте фазу замены так, чтобы она была достаточно длинной для завершения анимации. Этого можно добиться следующим способом:
<style>
.fade-me-out.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
<button class="fade-me-out"
hx-delete="/fade_out_demo"
hx-swap="outerHTML swap:1s">
Fade Me Out
</button>
Больше примеров анимации с HTMX можно посмотреть здесь
Расширения HTMX
HTMX предоставляет фреймворк расширений для создания и развёртывания расширений в приложениях на основе htmx. С помощью атрибута hx-ext
вы можете использовать библиотеки расширений (после их подключения в JavaScript).
Использование htmx расширения включает в себя два этапа:
- Включите определение расширения, что добавит его в реестр расширений HTMX.
- Атрибут
hx-ext
используется для указания расширения.
// Подключения библиотеки расширения
<script src="/path/to/ext/debug.js" defer></script>
// добавление расширения (debug) в htm-ext атрибут
<button hx-post="/example" hx-ext="debug">
This Button Uses The Force
</button>
HTMX поставляется с набором расширений, удовлетворяющих основные потребности разработчиков. В каждом выпуске эти расширения тестируются на htmx. Посмотреть список расширений, включённых в HTMX, можно здесь
Логирование и события в HTMX
События
HTMX оснащён надёжным механизмом событий, который также служит системой логирования.
Вы можете легко зарегистрировать событие HTMX используя:
Подход слушателя событий
document.body.addEventListener("htmx:load", function (evt) {
myJavascriptLib.init(evt.detail.elt);
});
HTMX хелперы
htmx.on("htmx:load", function (evt) {
myJavascriptLib.init(evt.detail.elt);
});
в обоих случаях событием является htmx:load
.
Событие htmx:load
срабатывает всякий раз, когда HTMX загружает элемент в DOM, и функционально оно не отличается от обычного события load
.
Вы также можете инициализировать стороннюю библиотеку с помощью события htmx:load
:
htmx.onLoad(function (target) {
myJavascriptLib.init(target);
});
Также можно настроить запрос с помощью событий.
document.body.addEventListener("htmx:configRequest", function (evt) {
evt.detail.parameters["auth_token"] = getAuthToken(); // добавление нового параметра в запрос
evt.detail.headers["Authentication-Token"] = getAuthToken(); // добавление нового заголовка в запрос
});
В приведённом выше коде используется событие htmx:configRequest
для изменения AJAX-запроса перед его отправкой:
Вы можете настроить поведение HTMX при замене, обработав событие htmx:beforeSwap
.
document.body.addEventListener("htmx:beforeSwap", function (evt) {
if (evt.detail.xhr.status === 404) {
// предупреждение пользователя о возникновении ошибки 404 (возможно,
// использование более удобного механизма, чем alert())
alert("Error: Could Not Find Resource");
} else if (evt.detail.xhr.status === 422) {
// Разрешаем подмену ответа 422, поскольку мы используем его как сигнал,
// что что форма была отправлена с плохими данными, и хотим выполнить
// рендеринг с ошибки
//
// установите isError в false, чтобы предотвратить вывода ошибок в консоль
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if (evt.detail.xhr.status === 418) {
// Если возвращается код ответа 418 (I'm a teapot), пере нацельте
// содержимое ответа на элемент с id `teapot`.
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
В приведённом выше коде мы обработали несколько кодов ошибок 400
-го уровня, которые обычно не приводят к замене в htmx.
Посмотреть другие события, поддерживаемые HTMX, можно здесь.
Логирование
В HTMX предусмотрена переменная htmx.logger
, которая при установке записывает в журнал каждое событие.
htmx.logger = function (elt, event, data) {
if (console) {
console.log(event, elt, data);
}
};
Заключение
В этой статье мы ознакомились с библиотекой HTMX. Мы продемонстрировали, что с помощью HTMX можно добиться функциональности Ajax, без использования JavaScript, что позволит вам легко создавать динамические приложения. HTMX может стать не просто новой библиотекой. Она может произвести революцию в подходе к интерактивности в веб-разработке.