Что такое htmx

Источник: «Introduction to HTMX»
В этой статье мы рассмотрим, что такое 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 следующим образом:

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>

Другие модификаторы триггера, которые вы можете использовать:

Посмотреть другие модификаторы, поддерживаемые HTMX, можно здесь

Фильтры триггеров

Они применяются с помощью заключения выражения JavaScript в квадратные скобки [] после имени события. Если выражение равно true, событие будет вызвано, в противном случае — нет.

<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Click the force
</div>

В htmx есть несколько специальных событий, которые можно использовать с hx-trigger:

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

Опрос

Вы можете использовать синтаксис 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;
}

Как HTMX обрабатывает переходы под капотом: Перед добавлением нового содержимого на страницу находятся элементы, соответствующие атрибуту id текущего содержимого. Это происходит, когда новое содержимое получено с сервера. Перед тем как произойдёт замена, свойства старого содержимого реплицируются на новый элемент, если для него найдено соответствие в обновлённом контенте. После этого новое содержимое заменяется, но старые значения атрибутов остаются. Новые значения атрибутов заменяются через время "успокоения" (по умолчанию 20 мс).

Замены вне группы

Атрибут hx-swap-oob в ответе html может быть использован для замены содержимого ответа непосредственно в DOM с помощью атрибута id:

<div id="message" hx-swap-oob="true">Dark Sidious</div>
Anakin Skywalker

Из приведённого выше кода, дополнительное содержимое будет подставлено в целевой элемент обычным способом, но div#message будет подставлен прямо в соответствующий элемент DOM в этом ответе.

Параметры

Вы можете фильтровать аргументы, которые будут переданы при вызове AJAX, с помощью атрибута hx-params.

Вот возможные значения этого атрибута:

<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>

Принцип работы hx-push-url: Прежде чем сделать запрос к /star-wars-characters, HTMX сделает снапшот текущего документа и сохранит его, когда пользователь перейдёт по этой ссылке. После этого новый адрес будет добавлен в стек истории, и обмен будет завершён.

HTMX имитирует "возврат" к предыдущему состоянию, когда пользователь нажимает кнопку "назад", извлекая старое содержимое из хранилища и заменяя его на целевое. HTMX отправит ajax-вызов на указанный URL с заголовком HX-History-Restore-call, установленным в true, если местоположение не будет найдено в кэше, и будет ожидать возвращения HTML, необходимого для полной страницы. В качестве альтернативы он принудительно обновит страницу в браузере, если параметр htmx.config.refreshOnHistoryMiss установлен в true.

Валидация с HTMX

Поскольку HTMX работает с HTML5 Validation API, недействительный валидируемый ввод не позволит выполнить запрос формы. Это относится как к WebSocket, так и к AJAX-запросам.

События, связанные с валидацией в HTMX, включают:

<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 расширения включает в себя два этапа:

// Подключения библиотеки расширения
<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 может стать не просто новой библиотекой. Она может произвести революцию в подходе к интерактивности в веб-разработке.

Дополнительные материалы

Предыдущая Статья

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

Следующая Статья

Новые возможности Laravel 11