Spec-Zone.ru › htmx

Документация

От установки до продвинутого использования.

htmx вкратце

htmx расширяет встроенную в HTML концепцию гипермедийных элементов управления.

Чтобы понять htmx, рассмотрим два наиболее известных таких элемента управления: <a> (якоря, также известные как «ссылки») и <form>.

Вот простой, ничем не примечательный тег-якорь:

<a href="/blog">Blog</a>

Если не обращать внимания на банальность этой ссылки, то перед нами поистине удивительная технология: когда пользователь нажимает на эту ссылку в браузере, браузер отправляет HTTP-запрос GET на /blog. Затем браузер загружает HTML-ответ сервера в окно браузера.

Формы немного сложнее:

<form method="post" action="/register">
    <label>Email: <input type="email"></label>
    <button type="submit">Submit</button>
</form>

Когда пользователь отправляет эту форму (например, нажимая кнопку «Отправить»), браузер отправляет HTTP-запрос POST на /register. И снова браузер загружает HTML-ответ на этот запрос в окно браузера.

Общий шаблон здесь таков: пользователь выполняет действие, например щелчок мышью, браузер отправляет HTTP-запрос на сервер, а затем загружает HTML-ответ в окно браузера.

Как htmx расширяет эту идею

Основная идея htmx заключается в том, чтобы с помощью нескольких пользовательских атрибутов обобщить эту идею:

  • Любой элемент может отправлять HTTP-запрос
  • Любое событие может запускать этот запрос
  • HTML-ответ можно поместить в любое место DOM

Это небольшое расширение значительно повышает выразительность HTML.

Вот пример кнопки, использующей htmx:

<button hx-post="/clicked"
        hx-trigger="click"
        hx-target="#output"
        hx-swap="outerHTML">
    Click Me
</button>
<output id="output"></output>

Атрибуты htmx начинаются с hx-. Рассмотрим каждый из них:

  • hx-post="/clicked" — эта кнопка должна отправлять POST-запрос на относительный URL /clicked
  • hx-trigger="click" — запрос нужно отправлять при нажатии кнопки
  • hx-target="#output" — HTML из ответа на этот запрос нужно поместить в элемент с идентификатором output
  • hx-swap="outerHTML" — нужно полностью заменить этот элемент на полученный HTML

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

Обратите внимание: hx-trigger здесь избыточен. Если его опустить, htmx использует событие-триггер по умолчанию, которым для кнопок является щелчок.

Важно понимать, что htmx ожидает от сервера HTML. В этом случае сервер вернёт фрагмент HTML, например <div>, чтобы заменить кнопку. htmx не ожидает JSON.

Поскольку htmx работает с HTML, он следует исходной модели веб-программирования, используя гипертекст как движок состояния приложения (HATEOAS).

Благодаря этому разработка с htmx больше напоминает традиционную веб-разработку, чем работа с большинством современных библиотек для фронтенда.

Установка htmx

htmx — это один файл JavaScript без зависимостей. Для его использования не требуется сборка.

Установка через CDN

Чтобы установить htmx как обычную библиотеку JavaScript через CDN jsdeliver, добавьте это в тег <head>:

<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0" integrity="sha384-BvJpBiO8Kh31EqtJe5DRIeWrHWnCGkwytKs9NKFi86Hhw96dEqdEMzZDeK9iEGTc" crossorigin="anonymous"></script>

Или, если вам нужна неминифицированная версия (например, для отладки), используйте:

<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/htmx.js" integrity="sha384-ESzWv77gBOAGtF3d7B8QiQ786cghRRsyhuYVVJGsrFGxwT0Dj1fLxReX9Ul6t4n9" crossorigin="anonymous"></script>

Модуль ES

Чтобы установить htmx как модуль JavaScript ES через CDN jsdeliver, добавьте это в тег <head>:

<script type="module" src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/htmx.esm.min.js" integrity="sha384-lIFlfAj002Phzxvrea858KMM3/dXelBFF5rq+Oh5lChznUWuVI0mabAEUnsRa/2W" crossorigin="anonymous"></script>

или неминифицированную версию:

<script type="module" src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/htmx.esm.js" integrity="sha384-fh3WoeSX2U60P2sV0M8Y6xvhTB5cpTb+AAF28P71BzGUZ/KI7QYrzKfwTFD/OCr6" crossorigin="anonymous"></script>

Скачивание и включение htmx в проект

Хотя CDN удобен, рекомендуем рассмотреть возможность самостоятельного размещения в рабочей среде.

  1. Скачайте htmx.min.js
  2. Сохраните его в проекте (например, в /js/htmx.min.js)
  3. Добавьте это в тег <head>:
<script src="/js/htmx.min.js"></script>

Этот способ иногда называют «включением» htmx в проект.

Также можно скачать:

  • htmx.js (неминифицированная версия)
  • htmx.esm.min.js (модуль ES)
  • htmx.esm.js (модуль ES, неминифицированная версия)

Установка htmx через NPM

Вы можете установить htmx с помощью стандартного

npm install htmx.org@4.0.0
import 'htmx.org';
import htmx from 'htmx.org';

// Now you can use htmx.ajax(), htmx.find(), etc.

Установка htmx и основных расширений: htmax.js

У htmx есть множество полезных расширений, добавляющих новые возможности.

Если вы хотите установить дистрибутив, в который уже включено большинство полезных расширений, чтобы не беспокоиться об этом самостоятельно, воспользуйтесь дистрибутивом htmax.js.

Переход с htmx 2.x на 4.x

Между htmx 2.x и 4.x есть три основных поведенческих изменения:

  • В htmx 2.0 наследование атрибутов по умолчанию неявное, а в 4.0 — явное
    • Чтобы восстановить поведение версии 2.0, задайте для настройки htmx.config.implicitInheritance значение true
  • В htmx 2.0 ответы с кодами 400 и 500 по умолчанию не заменяют содержимое, тогда как в htmx 4.0 эти запросы будут заменять содержимое
    • Чтобы восстановить поведение версии 2.0, задайте для настройки htmx.config.noSwap значение [204, 304, '4xx', '5xx']
  • В htmx 2.0 для переходов по истории использовался локальный снимок кэша, а в версии 4.0 отправляется запрос к серверу, чтобы получить полную страницу для восстановления
    • В htmx есть расширение hx-history-cache, позволяющее использовать локальный кэш. Это расширение без проблем интегрируется с Alpine.js и hx-live.

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

Средство проверки обновления

Чтобы упростить обновление, в htmx 4 включена утилита командной строки, которая сканирует шаблоны и файлы JS на наличие кода htmx 2, требующего обновления.

Она проверяет следующее:

  • Удалённые атрибуты
  • Старые названия событий
  • Шаблоны наследования атрибутов
  • Изменения в расширениях и т. д.

Запустить средство проверки обновления можно с помощью npx:

npx htmx.org@4.0.0 upgrade-check -- ./path/to/project/root

npx htmx.org@4.0.0 upgrade-check --ext .vue ./path/to/project/root

По умолчанию утилита сканирует файлы .html, .php, .js, .ts, .jinja, .jinja2, .j2, .erb и .hbs. С помощью аргумента --ext можно добавить расширения файлов, подобных HTML.

Результаты выводятся в формате file:line, ссылки в нём можно открывать в большинстве редакторов.

Подробнее см. в разделе Что нового в версии 4.0

Авторы расширений, которым нужно адаптировать свои расширения для htmx 4, могут обратиться к руководству Переход расширений на версию 4.0

Отправка запросов и обработка ответов

Суть htmx — отправлять HTTP-запросы в ответ на события, а затем помещать HTML-ответ в документ.

Основные атрибуты, управляющие этим поведением:

  • hx-get — отправляет HTTP-запрос GET
  • hx-post — отправляет HTTP-запрос POST
  • hx-put — отправляет HTTP-запрос PUT
  • hx-patch — отправляет HTTP-запрос PATCH
  • hx-delete — отправляет HTTP-запрос DELETE
  • hx-query — отправляет HTTP-запрос QUERY

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

<button hx-get="/info">
  Get Information
</button>

Начиная с htmx 4.0, можно также использовать следующие альтернативные атрибуты:

  • hx-action — URL, на который нужно отправить запрос
  • hx-method — используемый HTTP-метод
<button hx-action="/info" hx-method="GET">
  Get Information
</button>

Этот подход вдохновлён синтаксисом, используемым в формах:

<form action="/info" method="GET">
  <button>Get Information</button>
</form>

Запуск запросов

По умолчанию HTTP-запросы запускаются при «естественном» событии элемента:

  • input, textarea и select запускаются при событии change
  • form запускается при событии submit
  • всё остальное запускается событием click

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

Вот пример div, который отправляет HTTP-запрос POST на /mouse_entered, когда указатель мыши оказывается над ним:

<div hx-post="/mouse_entered" hx-trigger="mouseenter">
    Mouse Trap
</div>

Модификаторы триггеров

У триггера также могут быть дополнительные модификаторы, изменяющие его поведение.

Например, если запрос должен выполняться только один раз, можно использовать модификатор once для триггера:

<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
    Mouse Trap
</div>

Другие модификаторы триггеров:

Модификатор Описание
changed отправлять запрос, только если значение элемента изменилось
delay:<time interval> подождать указанное время (например, 1s) перед отправкой запроса. Если событие срабатывает снова, отсчёт начинается заново
throttle:<time interval> сразу отправить первый запрос, а затем подождать указанное время (например, 1s) перед отправкой следующего. Если за это время произойдут другие события, запрос будет отправлен по последнему из них в конце периода
from:<selector> слушать событие на другом элементе. Принимает CSS-селекторы и расширенные селекторы; используется, например, для сочетаний клавиш. Селектор не вычисляется повторно при изменении страницы
target:<selector> срабатывать, только если event.target соответствует заданному CSS-селектору. Полезно, когда событие прослушивается на контейнере, но нужны события только от определённых дочерних элементов
prevent вызвать event.preventDefault()
stop вызвать event.stopPropagation(). consume делает то же самое
halt сокращённая запись для prevent stop
capture слушать событие на этапе перехвата, сверху вниз, а не на этапе всплытия
passive сообщить браузеру, что обработчик не будет вызывать preventDefault(), чтобы браузер мог прокручивать страницу, не дожидаясь выполнения вашего кода

Обратите внимание: если селектор содержит пробелы в from или target, его нужно заключить в круглые скобки, например from:(form input).

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

С помощью триггеров можно реализовать множество распространённых шаблонов взаимодействия, например активный поиск:

<input type="text"
       name="q"
       placeholder="Search..."
       hx-query="/search"
       hx-trigger="input delay:500ms, keyup[key=='Enter']"
       hx-target="#search-results">
<div id="search-results"></div>

Этот элемент ввода отправит запрос QUERY через 500 миллисекунд после события ввода или при нажатии клавиши enter.

Элемент ввода вставляет полученный HTML в div с идентификатором search-results. (Обработка ответа описана ниже.)

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

В приведённом выше примере вы могли заметить квадратные скобки после названия события keyup. Это фильтр триггера.

Фильтры триггеров позволяют добавить после названия события фильтрующее выражение JavaScript, которое отменяет срабатывание триггера, если условия не выполнены. Чтобы отменить срабатывание, выражение должно возвращать false.

Вот пример, в котором триггер срабатывает, только если щёлкнуть по элементу с зажатой клавишей Shift

<div hx-get="/shift_clicked" hx-trigger="click[shiftKey]">
    Shift Click Me
</div>

Такие свойства, как shiftKey, сначала ищутся в объекте события-триггера, а затем в глобальной области видимости.

Символ this будет указывать на текущий элемент.

Обратите внимание: для фильтров триггеров требуется eval(), поэтому их не следует использовать при строгой политике безопасности содержимого.

Специальные события

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

  • load — срабатывает один раз при первой загрузке элемента
  • revealed — срабатывает один раз, когда элемент впервые прокручивается в область просмотра
  • intersect — срабатывает, когда элемент попадает в область просмотра. Добавьте модификатор once, чтобы событие сработало только один раз. Поддерживаются три дополнительных параметра:
    • root:<selector> — CSS-селектор корневого элемента для пересечения
    • rootMargin:<margin> — отступ вокруг корневого элемента
    • threshold:<float> — число с плавающей точкой от 0,0 до 1, указывающее, при какой доле пересечения срабатывает событие

Для запуска запросов также можно использовать пользовательские события. Создавайте их с помощью htmx.trigger() или с сервера, используя заголовок ответа HX-Trigger.

Опрос

Опрос — это простой метод, при котором веб-страница периодически отправляет запросы на сервер, чтобы проверить наличие обновлений. Это простой способ получать обновлённое содержимое с сервера. Он не требует постоянного подключения к серверу и хорошо переносит сетевые сбои.

В htmx опрос можно реализовать с помощью синтаксиса every в атрибуте hx-trigger:

<div hx-get="/news" hx-trigger="every 2s"></div>

Это указывает htmx:

Каждые 2 секунды отправлять GET-запрос на /news и загружать ответ в div

Опрос при загрузке

Ещё один способ реализовать опрос в htmx — «опрос при загрузке»: элемент задаёт триггер load вместе с задержкой и заменяет себя ответом:

<div hx-get="/messages"
     hx-trigger="load delay:1s"
     hx-swap="outerHTML">
</div>

Если конечная точка /messages продолжает возвращать настроенный таким образом div, запросы к URL будут отправляться каждую секунду.

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

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

Обработка ответов

htmx ожидает, что ответы на отправленные им HTTP-запросы будут содержать HTML. Этим он отличается от таких фронтенд-фреймворков, как React, которые используют ответы в формате JSON.

Обычно ответы htmx — это фрагменты HTML, то есть небольшие части HTML, а не полный документ:

<ul id="contacts">
  <li>Joe Blow</li>
  <li>Jane Doe</li>
</ul>

Затем htmx вставляет это содержимое в документ. Для этого ему нужны две вещи:

  • Целевой элемент — место, куда поместить содержимое
  • Стратегия замены — способ размещения содержимого

Этим управляют два атрибута: hx-target и hx-swap.

Выбор целевых элементов

По умолчанию ответы вставляются в элемент, отправивший запрос. Это можно изменить с помощью атрибута hx-target, который принимает CSS-селектор, указывающий целевой элемент:

<button hx-get="/info"
        hx-target="#output">
  
</button>
<output id="output">-</output>
Выбор цели с помощью расширенных селекторов

Помимо обычных селекторов, htmx поддерживает синтаксис расширенных селекторов. Этот синтаксис расширяет возможности таких атрибутов, как hx-target:

Селектор Соответствующие элементы Пример
<CSS selector> Те же элементы, что возвращает querySelectorAll(). hx-target="#results"
this Сам элемент (цель по умолчанию) hx-target="this"
closest <CSS selector> Ближайший родительский элемент, соответствующий селектору. hx-target="closest .card"
find <CSS selector> Первый дочерний элемент, соответствующий селектору. hx-target="find .username"
next Следующий соседний элемент. hx-target="next"
next <CSS selector> Первый подходящий элемент после этого элемента в порядке документа. hx-target="next .results"
previous Предыдущий соседний элемент. hx-target="previous"
previous <CSS selector> Первый подходящий элемент перед этим элементом в порядке документа. hx-target="previous .results"
body Тело документа. hx-target="body"
document Документ. hx-trigger="click from:document"
window Окно. hx-trigger="scroll from:window"
host Хост теневого DOM. Полезен внутри веб-компонента. hx-target="host"

Относительные селекторы особенно полезны для упрощения DOM:

<button hx-get="/info"
        hx-target="next output">
  
</button>
<output>-</output>

Здесь мы используем относительный селектор next, чтобы выбрать следующий элемент output без необходимости задавать идентификатор. Этот приём особенно полезен при работе с повторяющимся содержимым, например таблицей, когда нужно избежать генерации идентификаторов для правильной работы целевых элементов.

Настройка замены

Атрибут hx-swap управляет тем, как содержимое вставляется в целевой элемент или вместо него.

По умолчанию используется способ замены innerHTML: htmx помещает содержимое ответа внутрь целевого элемента, а не заменяет его. Это соответствует принципу работы встроенных в HTML iframe.

Доступны и другие алгоритмы замены:

Название Описание
innerHTML способ по умолчанию: помещает содержимое внутрь целевого элемента
outerHTML заменяет целевой элемент целиком полученным содержимым
outerSync изменяет атрибуты целевого элемента, а затем заменяет его дочерние элементы. Целевой элемент остаётся в DOM
before (или beforebegin) добавляет содержимое перед целевым элементом в его родительском элементе
prepend (или afterbegin) добавляет содержимое перед первым дочерним элементом целевого элемента
append (или beforeend) добавляет содержимое после последнего дочернего элемента целевого элемента
after (или afterend) добавляет содержимое после целевого элемента в его родительском элементе
delete удаляет целевой элемент независимо от ответа
none не добавляет содержимое ответа (при этом по-прежнему обрабатываются внеполосная замена и заголовки ответа)
innerMorph обновляет дочерние элементы целевого элемента, сохраняя как можно большую часть существующего DOM
outerMorph обновляет сам целевой элемент, сохраняя как можно большую часть существующего DOM
textContent задаёт текстовое содержимое целевого элемента (без разбора HTML)

Например, afterend вставляет ответ после целевого элемента, а не внутрь него. Так работает кнопка «Загрузить ещё»: она добавляет новые строки, а затем заменяет саму себя:

<tr id="row-20">
  <td>Row 20</td>
  <td>
    <button hx-get="/rows?page=2"
            hx-target="closest tr"
            hx-swap="outerHTML">
      Load More
    </button>
  </td>
</tr>
Сохранение содержимого при замене

Некоторые элементы должны оставаться неизменными при замене, например воспроизводимое видео или сторонний виджет. Добавьте hx-preserve, чтобы сохранить элемент в исходном виде при замене родительского элемента:

<div id="results">
  <video id="player" hx-preserve></video>
</div>

Сохранённые элементы сопоставляются по id. Задайте элементу стабильное значение id и включите такое же значение id в ответ.

Замена с преобразованием

В htmx 4 появились встроенные способы замены innerMorph и outerMorph. Ранее замена с преобразованием была доступна только через расширение Idiomorph.

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

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

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

Параметры замены

Атрибут hx-swap также поддерживает параметры для настройки поведения замены в htmx.

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

Это поведение можно отключить, установив модификатор ignoreTitle в значение true:

<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>

Для hx-swap доступны следующие модификаторы:

Параметр Описание
swap интервал времени (например, 100ms, 1s), на который откладывается замена
settle интервал времени, на который откладывается этап фиксации, выполняющийся после замены. По умолчанию — 1ms
transition true или false: использовать ли API переходов между состояниями представления для этой замены
ignoreTitle если установлено значение true, заголовок из нового содержимого игнорируется, а заголовок документа не обновляется
strip true или false: удалять ли внешний элемент при замене (разворачивать содержимое)
focusScroll true или false: прокручивать ли страницу к элементам, получившим фокус
swapEmpty true или false: выполнять ли основную замену, если тело ответа пустое (false пропускает её).
scroll top или bottom: прокрутить целевой элемент к его верхнему или нижнему краю
show top или bottom: прокрутить верхний или нижний край целевого элемента в область просмотра
target селектор для перенаправления замены на другой элемент

Все модификаторы замены указываются после стиля замены.

Подробнее об этих параметрах см. в документации по hx-swap.

Выбор содержимого ответа

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

Атрибут hx-select принимает обычный CSS-селектор и извлекает только соответствующее ему содержимое для замены:

<button hx-get="/info" hx-target="#result" hx-select="#info-detail">
  Get Info
</button>

<div id="result"></div>

Выбор «внеполосного» содержимого

Если нужно выполнить дополнительную замену на основе большого фрагмента содержимого, полученного от сервера, используйте атрибут hx-select-oob:

<button hx-get="/dashboard"
        hx-target="#main"
        hx-select="#main-content"
        hx-select-oob="#alert,#sidebar">
  Refresh
</button>

Здесь, помимо выбора для основной замены элемента с идентификатором main, htmx находит в содержимом ответа элементы с идентификаторами alert и sidebar и заменяет каждый из них элементом с таким же id на текущей странице.

Формы

Работать с формами и элементами ввода в htmx естественно, если вы знакомы с обычным HTML.

Значения элементов ввода

По умолчанию элемент, инициирующий запрос, включает своё значение value, если оно у него есть.

Если элемент — это form, он включает значения всех inputs внутри себя.

Если элемент отправляет запрос с телом (то есть любой запрос, кроме GET и DELETE), в запрос включаются значения всех inputs связанной формы (обычно это ближайшая охватывающая форма, но она может быть другой, например, если используется атрибут form).

<form>
  <input name="email" value="joe@example.com">
  <input name="plan" value="pro">
  <button hx-post="/signup">Sign Up</button>
</form>

Кнопка отправляет запрос POST на /signup с email=joe@example.com&plan=pro.

Добавление других значений

Если нужно включить в запрос значения других элементов, используйте атрибут hx-include.

Атрибут hx-include принимает расширенный CSS-селектор и включает значения всех подходящих элементов.

Чтобы отправлять вычисляемые значения (а не значения, содержащиеся в элементах ввода), используйте атрибут hx-vals.

<button hx-post="/save" hx-vals='{"draft": true}'>Save Draft</button>

Можно использовать префикс js:, чтобы вычислять значение динамически при отправке запроса:

<button hx-post="/save" hx-vals='js:{scrollY: window.scrollY}'>Save</button>

Загрузка файлов

По умолчанию htmx использует стандартный формат application/x-www-form-urlencoded для отправки значений на сервер.

Чтобы загружать файлы через запрос htmx, задайте атрибуту hx-encoding значение multipart/form-data.

Для отправки запроса будет использоваться объект FormData, который корректно включает файл в запрос.

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

Полный пример см. в шаблоне загрузки файлов.

Проверка форм

htmx интегрируется с API проверки HTML5 и не отправляет запрос для формы, если её поле с проверкой содержит недопустимое значение.

По умолчанию элементы вне форм не проверяются перед отправкой запросов, но проверку можно включить, установив для их атрибута hx-validate значение «true».

Наследование атрибутов

Изменения в htmx 4.0

В htmx 2.0 наследование атрибутов по умолчанию было неявным: элементы наследовали атрибуты родительских элементов, например hx-target. В htmx 4.0 наследование атрибутов по умолчанию стало явным и выполняется с помощью модификатора :inherited.

Наследование атрибутов позволяет «поднимать» атрибуты выше по DOM, чтобы избежать дублирования кода.

Рассмотрим следующий HTML:

<button hx-delete="/account" hx-confirm="Are you sure?">
    Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
    Update My Account
</button>

Здесь атрибут hx-confirm дублируется.

Мы можем поднять этот атрибут на родительский элемент, используя модификатор :inherited:

<div hx-confirm:inherited="Are you sure?">
    <button hx-delete="/account">
        Delete My Account
    </button>
    <button hx-put="/account">
        Update My Account
    </button>
</div>

Этот атрибут hx-confirm теперь будет применяться ко всем элементам htmx внутри него.

Добавление к унаследованному значению

По умолчанию атрибут дочернего элемента переопределяет унаследованное значение этого атрибута. Вместо этого можно использовать модификатор :append, чтобы добавить значение дочернего элемента к унаследованному:

<div hx-vals:inherited="tenant:acme">
    ...
    <button hx-post="/save" hx-vals:append="source:save-btn">Save</button>
</div>

Если ни один предок не задаёт значение, используется только добавленное значение.

Можно сочетать :inherited и :append на одном элементе, если вы хотите, чтобы дочерние элементы наследовали новое значение.

Обновление нескольких целевых элементов

Обычно запросы htmx обновляют один целевой элемент. Иногда требуется одновременно обновить несколько частей страницы.

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

htmx предоставляет два механизма для обновления нескольких целевых элементов одним ответом:

  • Внеполосная подмена
  • Частичные фрагменты (новинка в htmx 4)

Внеполосная подмена

Внеполосная подмена (OOB) позволяет подменить отдельный элемент в DOM по его идентификатору, переместив его из исходного места в ответе в другое место.

Для этого можно добавить hx-swap-oob="true" к элементу в ответе.

В этом случае htmx найдёт в DOM элемент с таким же id на странице и подменит его.

Например, если сервер вернул это содержимое в ответ на отправку формы:

<div id="message" hx-swap-oob="true">
    Form submitted successfully!
</div>

<form id="my-form">
    <!-- Updated form content -->
</form>

Тогда элемент form будет подменён в DOM обычным способом, в соответствии с атрибутами hx-target и hx-swap, а div будет подменён «внеполосно», заменив элемент DOM с идентификатором message, расположенный в другом месте страницы.

Настройка внеполосной подмены

Можно задать способ подмены для OOB, указав допустимую стратегию подмены в значении hx-swap-oob:

<div id="notifications" hx-swap-oob="beforeend">
    <span>New notification</span>
</div>

В этом случае содержимое добавляется в div#notifications, а не заменяет его.

Ответы, состоящие только из OOB

Перед основной подменой htmx удаляет OOB-элементы из ответа. Если после этого ничего не остаётся, основная подмена пропускается, а содержимое основного целевого элемента сохраняется.

<!-- Server returns only OOB content: the main target is left untouched -->
<div id="notifications" hx-swap-oob="true">
    <span class="badge">5</span>
</div>

Это поведение можно изменить двумя настройками. Установите htmx.config.allowEmptySwapAfterOOB в true, чтобы основная подмена выполнялась в любом случае:

<meta name="htmx-config" content="allowEmptySwapAfterOOB:true">

Или задайте модификатор swapEmpty для отдельного элемента — он имеет приоритет над конфигурацией:

<button hx-post="/submit" hx-swap="outerHTML swapEmpty:true">Submit</button>

Обратите внимание: элементы <hx-partial> всегда пропускают пустую основную подмену, и ни одна из настроек этого не меняет. См. раздел Ответы, состоящие только из <hx-partial>.

Частичные фрагменты (<hx-partial>)

Тег hx-partial появился в htmx 4 и решает проблемы, с которыми мы сталкивались при использовании OOB-подмен на протяжении многих лет.

Чтобы использовать тег hx-partial, просто заключите в него содержимое.

Затем можно использовать обычные атрибуты htmx, чтобы точно указать, как подменить это содержимое в DOM:

<hx-partial hx-target="#messages" hx-swap="beforeend">
    <div class="message">New message content</div>
</hx-partial>

<hx-partial hx-target="#notifications">
    <span class="badge">5</span>
</hx-partial>

<form id="my-form">
    <!-- Main form content -->
</form>

Для тегов <hx-partial> можно задавать следующие атрибуты:

  • hx-target — CSS-селектор, указывающий, куда поместить содержимое
  • id — сокращённая альтернатива hx-target. Указывает элемент с заданным идентификатором (например, <hx-partial id="messages"> указывает на #messages)
  • hx-swap — необязательный атрибут. Способ подмены (по умолчанию — innerHTML)

Некоторые серверные шаблонизаторы удаляют неизвестные им теги. В этом случае используйте эквивалентную форму <template>:

<template hx type="partial" hx-target="#messages" hx-swap="beforeend">
    <div class="message">New message content</div>
</template>

Ответы, состоящие только из <hx-partial>

Если ответ содержит только элементы <hx-partial> и не содержит основного содержимого, htmx не выполняет основную подмену: предполагается, что вы хотите выполнить только частичную замену содержимого ответа.

<!-- Server returns only partials: the main target is left untouched -->
<hx-partial hx-target="#notifications">
    <span class="badge">5</span>
</hx-partial>
<hx-partial hx-target="#messages" hx-swap="beforeend">
    <div class="message">New message</div>
</hx-partial>

Если нужно очистить основной целевой элемент, добавьте swapEmpty:true к hx-swap на инициирующем элементе:

<button hx-post="/submit" hx-swap="outerHTML swapEmpty:true">Submit</button>

OOB и <hx-partial>

OOB-подмены предназначены для простой прямой замены элементов в DOM. Они подходят, когда нужно непосредственно заменить один элемент другим.

Частичные фрагменты универсальнее, но и сложнее. Они подходят, когда нужно заменить произвольное содержимое (а не только содержимое, привязанное к идентификатору) содержимым любого типа. Поскольку содержимое для замены находится внутри тега <hx-partial>, оно может иметь произвольную сложность (например, состоять из нескольких элементов верхнего уровня).

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

Синхронизация запросов

Часто требуется согласовать запросы двух элементов. Например, запрос одного элемента может отменять запрос другого или ожидать его завершения.

Для этого в htmx предусмотрен атрибут hx-sync.

Рассмотрим ситуацию с возможной гонкой между отправкой формы и запросом проверки отдельного поля ввода в этом HTML:

<form hx-post="/store">
    <input id="title" name="title" type="text"
           hx-post="/validate"
           hx-trigger="change">
    <button type="submit">Submit</button>
</form>

Если не использовать hx-sync, заполнение поля ввода и немедленная отправка формы вызовут два параллельных запроса к /validate и /store.

Если использовать hx-sync="closest form" для поля ввода и hx-sync="this:replace" для формы, htmx будет отслеживать запросы формы и отменит выполняющийся запрос поля ввода:

<form hx-post="/store" hx-sync="this:replace">
    <input id="title" name="title" type="text"
           hx-post="/validate"
           hx-trigger="change"
           hx-sync="closest form">
    <button type="submit">Submit</button>
</form>

Это декларативным способом решает проблему синхронизации двух элементов.

Отмена запроса

htmx также позволяет отменять запросы программно: для отмены всех выполняющихся запросов можно отправить элементу событие htmx:abort:

<button id="request-button" hx-post="/example">
    Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
    Cancel Request
</button>

Дополнительные примеры и сведения приведены на странице атрибута hx-sync.

Индикаторы запросов

Когда htmx отправляет HTTP-запрос, часто полезно сообщить пользователю, что выполняется какое-то действие.

В htmx для этого можно использовать специальный класс htmx-indicator.

Класс htmx-indicator определён в htmx так, что непрозрачность любого элемента с этим классом по умолчанию равна 0: элемент невидим, но остаётся в DOM.

При отправке запроса htmx добавит класс htmx-request к элементу (к инициирующему запрос элементу или к другому элементу, если он указан).

Класс htmx-request заставит дочерний элемент с классом htmx-indicator плавно изменить непрозрачность на 1, чтобы показать индикатор.

<button hx-get="/click">
    Click Me!
    <img class="htmx-indicator" src="/spinner.gif" alt="Loading...">
</button>

При отправке запроса этой кнопкой к ней будет добавлен класс htmx-request.

Это покажет расположенный внутри неё GIF-файл со значком загрузки.

Пользовательский CSS индикатора запроса

Класс htmx-indicator использует свойство opacity, чтобы скрывать и показывать индикатор выполнения. Если вы предпочитаете другой способ, можно создать собственный CSS-переход, например:

.htmx-indicator {
    display: none;
}

.htmx-request .htmx-indicator {
    display: inline;
}

.htmx-request.htmx-indicator {
    display: inline;
}

Указание конкретного индикатора

Чтобы добавлять класс htmx-request к другому элементу, используйте атрибут hx-indicator с расширенным CSS-селектором:

<div>
    <button hx-get="/click" hx-indicator="#indicator">
        Click Me!
    </button>
    <img id="indicator" class="htmx-indicator" src="/spinner.gif" alt="Loading..."/>
</div>

Отключение элементов

Ещё одна распространённая задача — отключить элементы на время выполнения запроса, чтобы пользователь не мог с ними взаимодействовать.

Чтобы отключить элементы на время запроса, можно использовать атрибут hx-disable и добавить к ним атрибут disabled:

<button hx-post="/submit" hx-disable="this">Submit</button>

Значение представляет собой расширенный селектор, поэтому можно отключать и другие элементы. В этом примере на время выполнения запроса отключается весь fieldset:

<fieldset>
  <input name="email">
  <button hx-post="/submit" hx-disable="closest fieldset">Submit</button>
</fieldset>

Подтверждение действий пользователем

Часто перед отправкой запроса необходимо подтвердить действие. В htmx предусмотрен атрибут hx-confirm, позволяющий подтвердить действие с помощью простого диалогового окна JavaScript:

<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
    Delete My Account
</button>

Значение hx-confirm также может содержать JavaScript, если использовать префикс js: или javascript:. В этом случае JavaScript будет выполнен, а если он вернёт промис, htmx дождётся его разрешения со значением true, прежде чем продолжить.

<script>
    async function swalConfirm() {
        let result = await Swal.fire({
            title: "Are you sure?",
            text: "You won't be able to revert this!",
            icon: "warning",
            showCancelButton: true,
            confirmButtonColor: "#3085d6",
            cancelButtonColor: "#d33",
            confirmButtonText: "Yes, delete it!"
        })
        return result.isConfirmed
    }
</script>
<button hx-delete="/account" hx-confirm="js:swalConfirm()">
    Delete My Account
</button>

Анимация подмены

Существует два способа анимировать элементы при подмене ответа в DOM с помощью htmx:

  • CSS-переходы
  • Переходы представлений

Обратите внимание: анимация может выглядеть интересно, но не должна ухудшать удобство использования. Как правило, её длительность должна быть меньше 100 миллисекунд.

CSS-переходы

CSS-переходы — хорошо зарекомендовавший себя механизм анимации содержимого DOM.

При подмене htmx применяет CSS-переходы к любому содержимому с неизменным идентификатором, независимо от выбранного способа подмены.

Например, если исходное содержимое:

<div id="div1">Original Content</div>

заменяется новым содержимым:

<div id="div1" class="red">New Content</div>

между ними можно задать CSS-переход следующим образом:

.red {
    color: red;
    transition: all ease-in 100ms;
}

Переходы представлений

Более новый метод анимации — это API переходов представлений, который позволяет разработчикам создавать структурированные анимированные переходы между разными состояниями DOM.

Переходы представлений гораздо сложнее (и намного изощрённее!), чем CSS-переходы, но дают значительно больше контроля над анимацией.

htmx поддерживает переходы представлений следующими способами:

  • Если глобально установить htmx.config.transitions в true, переходы представлений будут включены для всех подмен
  • Для отдельной подмены — с помощью параметра hx-swap="outerHTML transition:true" атрибута hx-swap transition:
  • Для элементов с ускоренной обработкой — с помощью параметра перехода: hx-boost="transition:true"

Обратите внимание: стандартный переход представления — это перекрёстное затухание длительностью 250 миллисекунд, которое, на наш взгляд, очень плохо подходит для подмены. Поэтому при использовании переходов представлений с htmx рекомендуется переопределить его.

Ускоренная обработка ссылок и форм

В htmx можно «ускорить» обычные HTML-ссылки и формы с помощью атрибута hx-boost.

Этот атрибут преобразует теги ссылок и формы в запросы на основе fecth(), которые по умолчанию нацелены на body страницы.

Пример:

<div hx-boost:inherited="true">
    <a href="/blog">Blog</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
</div>

Теги ссылок в этом div будут отправлять AJAX-запрос GET к /blog и подменять тег body полученным ответом.

Обратите внимание, что здесь в hx-boost используется модификатор inherited.

Преимущества и недостатки ускоренной обработки

Ускоренная обработка — это функция, которая присутствует в htmx ещё со времён, когда проект назывался intercooler. Раньше у неё было несколько существенных преимуществ:

  • Она устраняла мигание неоформленного содержимого (FOUC)
  • Она позволяла использовать CSS-переходы между страницами
  • Она избавляла от необходимости повторно разбирать CSS/JS между страницами
  • Она позволяла сохранять элементы с помощью hx-preserve

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

Это уменьшило преимущества ускоренной обработки. Она по-прежнему даёт выигрыш в производительности и остаётся единственным способом использовать CSS-переходы и сохранять элементы при навигации.

Иногда пользователи сталкиваются с недостатком этого подхода (который отчасти и обеспечивает его быстродействие): ускоренная обработка не сбрасывает среду JavaScript. При обычной навигации браузер полностью сбрасывает среду JavaScript.

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

В сообществе htmx к ускоренной обработке относятся неоднозначно. Одни пользователи её любят, другие не рекомендуют её использовать.

К слову, на этом сайте документации мы используем ускоренную обработку.

Ускоренная обработка и прогрессивное улучшение

Преимущество hx-boost в том, что при отключённом JavaScript сайт продолжает работать: ссылки и формы сохраняют свою функциональность, просто не используют AJAX-запросы.

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

Другие шаблоны htmx тоже можно адаптировать для прогрессивного улучшения, но для этого потребуется дополнительное обдумывание.

Рассмотрим пример активного поиска. В текущем виде он не обеспечивает корректную деградацию: пользователи с отключённым JavaScript не смогут воспользоваться этой функцией. Это сделано для простоты, чтобы пример был как можно короче.

Однако можно поместить поле ввода с htmx в элемент формы:

<form action="/search" method="POST">
    <input class="form-control" type="search"
           name="search" placeholder="Begin typing to search users..."
           hx-query="/search"
           hx-trigger="keyup changed delay:500ms, search"
           hx-target="#search-results"
           hx-indicator=".htmx-indicator">
</form>

В таком случае пользователи с включённым JavaScript по-прежнему получат удобный интерфейс активного поиска, а пользователи без JavaScript смогут нажать клавишу Enter и выполнить поиск.

Ещё лучше добавить кнопку «Поиск». Тогда потребуется обновить форму, добавив атрибут hx-post, соответствующий атрибуту action, или, возможно, использовать для неё hx-boost.

На стороне сервера потребуется проверять заголовок HX-Request, чтобы отличать запросы htmx от обычных и определять, что именно отправлять клиенту.

Аналогичным образом можно адаптировать и другие шаблоны, чтобы обеспечить прогрессивное улучшение в приложении.

Как видите, для этого требуется больше обдумывания и работы. Кроме того, некоторые функции могут стать совершенно недоступными. Решения о таких компромиссах должен принимать разработчик с учётом целей проекта и его аудитории.

Доступность

Доступность тесно связана с прогрессивным улучшением. Использование таких методов прогрессивного улучшения, как hx-boost, сделает ваше приложение на htmx более доступным для широкого круга пользователей.

Приложения на htmx очень похожи на обычные веб-приложения, не использующие fetch(), поскольку htmx ориентирован на HTML.

Поэтому к ним применимы обычные рекомендации по обеспечению доступности HTML. Например:

  • По возможности используйте семантическую разметку HTML (то есть подходящие теги для соответствующих элементов)
  • Обеспечьте хорошую видимость состояния фокуса
  • Свяжите текстовые подписи со всеми полями формы
  • Повышайте удобочитаемость приложения с помощью подходящих шрифтов, контрастности и других средств

Поддержка истории браузера

Изменения в htmx 4.0

Поддержка истории в htmx 4.0 значительно изменилась. Теперь мы не сохраняем снимок DOM в sessionStorage.

Вместо этого при каждом переходе к элементу истории отправляется запрос на загрузку всей страницы. Такой подход гораздо надёжнее и устойчивее к ошибкам. Он также устраняет проблемы безопасности, связанные с хранением состояния истории в доступном хранилище.

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

Htmx предоставляет простой механизм взаимодействия с API истории браузера:

Чтобы URL запроса элемента отображался в адресной строке браузера, а текущее состояние страницы добавлялось в историю браузера, укажите атрибут hx-push-url:

<a hx-get="/blog" hx-push-url="true">Blog</a>

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

При нажатии кнопки «Назад» htmx загрузит прежнее содержимое по исходному URL и подменит им содержимое body, имитируя возврат к предыдущему состоянию.

ПРИМЕЧАНИЕ: Если вы добавляете URL в историю, по этому URL обязательно должна открываться полноценная страница! Пользователь может скопировать URL и отправить его по электронной почте или открыть в новой вкладке.

Замена текущего URL

Чтобы изменить URL без обновления истории, используйте атрибут hx-replace-url:

<a hx-get="/account" hx-replace-url="true">My Account</a>

Заголовки ответа для управления историей

Сервер может переопределить любой из этих атрибутов для отдельного ответа с помощью заголовков ответа HX-Push-Url и HX-Replace-Url.

Восстановление только части страницы

По умолчанию при переходе пользователя назад или вперёд htmx заменяет весь body.

Чтобы восстанавливать историю только внутри определённого элемента, используйте атрибут hx-history-elt:

<body>
    <nav><!-- never replaced --></nav>

    <main hx-history-elt>
        <h1>Page 1</h1>
    </main>
</body>

При переходе по истории htmx запрашивает URL, выбирает из ответа элемент hx-history-elt и подменяет им текущий элемент, оставляя остальную часть страницы без изменений.

Настройка истории

Параметр htmx.config.history позволяет настроить работу истории:

Значение Поведение
true значение по умолчанию. htmx запрашивает URL и подменяет содержимое ответом
"reload" htmx перезагружает всю страницу вместо отправки запроса
false htmx совсем не обрабатывает историю. Браузер работает обычным образом

Чтобы восстановить поведение htmx 2.x, при котором история восстанавливается из локального снимка, а не по полному запросу к серверу, используйте расширение hx-history-cache.

Продвинутые методы работы с запросами и ответами

Стандартного поведения htmx при работе с запросами и ответами часто достаточно, но иногда может потребоваться более сложная обработка HTTP. В этом разделе описано, как это сделать.

Обработка кодов HTTP-ответов

По умолчанию htmx подменяет в DOM все полученные ответы, кроме ответов с кодами HTTP-ответов 204 или 304.

Если сервер вернёт ответ с кодом 204 - No Content, htmx проигнорирует его содержимое и ничего не подменит, даже если ответ содержит тело.

Если код ответа равен 400 или 500, htmx вызовет событие htmx:response:error.

Настройка обработки кодов ответов

Это поведение можно настроить с помощью атрибута hx-status, которому после двоеточия указывается шаблон кода ответа:

Сначала htmx проверяет точный код, затем двухзначный шаблон с подстановочным знаком (например, 50x), а затем однозначный шаблон с подстановочным знаком (например, 5xx).

Пример:

<form hx-post="/submit"
      hx-target="#result"
      hx-status:422="target:#validation-errors"
      hx-status:5xx="target:#server-error"
      hx-status:503="swap:none">
    <input name="email">
    <button type="submit">Submit</button>
</form>

<div id="result"></div>
<div id="validation-errors"></div>
<div id="server-error"></div>

Это сообщает htmx следующее:

  • Успешные ответы (2xx) подменяются в #result (поведение по умолчанию)
  • Ответы 422 подменяются в #validation-errors
  • Ответы 503 не подменяются
  • Все остальные ответы 500 5xx подменяются в #server-error

Для глобальной настройки обработки кодов ответов можно также использовать параметр конфигурации htmx.config.noSwap.

Например, чтобы вернуть поведение htmx 2.0, при котором ответы с кодами 4xx и 5xx не подменяются, добавьте следующую конфигурацию:

<meta name="htmx-config" content='{"noSwap": [204, 304, "4xx", "5xx"]}'>

Заголовки запросов

htmx добавляет к отправляемым запросам следующие заголовки:

Заголовок Описание
HX-Boosted указывает, что запрос отправлен элементом, использующим hx-boost
HX-Current-URL текущий URL браузера
HX-Request всегда равен «true»
HX-Request-Type "partial" для подмен с заданной целью, "full" для запросов на уровне body или hx-select
HX-Source исходный элемент в формате tag#id (например, button#submit)
HX-Target целевой элемент в формате tag#id (например, div#results)

При повторной загрузке страницы после промаха в кэше истории htmx также отправляет заголовок HX-History-Restore-Request. Подробнее см. раздел Поддержка истории браузера.

Добавление собственных заголовков

Чтобы добавить заголовки к запросу, используйте атрибут hx-headers.

<div hx-get="/data" hx-headers='{"X-Widget-Id": "42"}'>Get Data</div>

Чтобы вычислять заголовки во время отправки запроса, можно использовать префикс js:.

<div hx-get="/data" hx-headers='js:{"X-Scroll": window.scrollY}'>Get Data</div>

В этом примере при каждом запросе htmx отправляется токен CSRF.

<body hx-headers:inherited='js:{"X-CSRF-Token": getCsrfToken()}'>
  ...
</body>

Чтобы задавать заголовки программно, используйте событие htmx:config:request.

Заголовки ответов

htmx поддерживает следующие заголовки ответов:

Заголовок Описание
HX-Location позволяет выполнить перенаправление на стороне клиента без полной перезагрузки страницы
HX-Push-Url добавляет новый URL в стек истории
HX-Redirect позволяет перенаправить пользователя на новое место на стороне клиента
HX-Refresh если задано значение «true», клиент полностью обновит страницу
HX-Replace-Url заменяет текущий URL в адресной строке
HX-Reswap позволяет указать способ подмены ответа. Возможные значения см. в описании атрибута hx-swap
HX-Retarget CSS-селектор, который задаёт другой элемент страницы в качестве цели обновления содержимого
HX-Reselect CSS-селектор, позволяющий выбрать часть ответа для подмены. Переопределяет существующий атрибут hx-select на инициирующем элементе
HX-Trigger позволяет вызывать события на стороне клиента

Заголовки ответа HX-Trigger могут быть особенно полезны: они позволяют запускать клиентский код JavaScript с сервера.

Настройка отдельных запросов с помощью hx-config

Атрибут hx-config позволяет точно настроить параметры запроса, отправляемого htmx:

<button hx-post="/api/users" hx-config="timeout:5s">Create User</button>

Большинство параметров hx-config напрямую соответствуют параметрам запросов Fetch API:

Параметр Описание
timeout отменяет запрос по истечении указанного времени. Принимает значения 500ms, 5s, 2m или число миллисекунд. По умолчанию используется htmx.config.defaultTimeout
credentials "omit", "same-origin" или "include". По умолчанию — "same-origin"
cache режим кэширования Fetch API, например "no-cache" или "reload"
redirect "follow", "error" или "manual"
referrer URL источника перехода или "no-referrer"
integrity значение проверки целостности подресурсов
validate true для проверки формы перед отправкой запроса htmx. См. раздел Проверка формы

Параметр mode недоступен

hx-config не разрешает использовать параметр mode по соображениям безопасности.

htmx всегда сбрасывает mode до htmx.config.mode, значение которого по умолчанию — "same-origin".

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

Дополнительные сведения см. в разделе Вопросы безопасности.

Потоковая передача HTML

Для более интерактивных сценариев, в которых сервер отправляет DOM несколько обновлений в рамках одного запроса, htmx предоставляет различные расширения для потоковой передачи HTML.

Расширения потоковой передачи, предоставляемые htmx, используют:

  • События, отправленные сервером (SSE)
  • Веб-сокеты
  • Многочастные ответы

SSE

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

Расширение hx-sse поддерживает замену содержимого с помощью этих событий.

Рассмотрим следующую кнопку:

<button hx-post="/generate"
        hx-target="next output"
        hx-swap="append">
  Generate
</button>

<output></output>

Если установлено расширение htmx SSE, содержимое безымянных событий, получаемых от сервера, будет добавляться в выходной тег. Это обеспечивает естественный механизм потоковой вставки содержимого в элемент по мере его поступления.

Подробнее см. в документации расширения hx-sse.

Веб-сокеты

В отличие от SSE, веб-сокеты поддерживают открытое соединение в обоих направлениях, поэтому сервер и браузер могут отправлять сообщения в любое время.

Расширение hx-ws поддерживает замену содержимого этими сообщениями, а также отправку сообщений на сервер из элементов DOM.

Рассмотрим следующее окно чата:

<div hx-ws:connect="/chat"
     hx-target="#messages"
     hx-swap="append">

  <div id="messages"></div>

  <form hx-ws:send>
    <input name="message">
    <button>Send</button>
  </form>

</div>

Если установлено расширение htmx Web Socket, соединение открывается при загрузке, а каждое сообщение сервера добавляется в #messages. Форма отправляет свои значения обратно через то же соединение в формате JSON, поэтому новый запрос не создаётся.

Подробнее см. в документации расширения hx-ws.

Многочастные ответы

Ответ multipart/mixed содержит множество частей в одном теле; разделитель выбирается сервером:

HTTP/1.1 200 OK
Content-Type: multipart/mixed; boundary=...

Hello
--...
Content-Type: text/html

, world!

Расширение hx-multipart поддерживает замену содержимого этих частей в DOM по мере их поступления.

Рассмотрим кнопку генерации, которую мы видели в примере с SSE:

<button hx-post="/generate"
        hx-target="next output"
        hx-swap="append">
  Generate
</button>

<output></output>

Если установлено расширение htmx multi-part и сервер отвечает запросом типа multipart/mixed;, содержимое частей, получаемых от сервера, будет добавляться в выходной тег.

Подробнее см. в документации расширения hx-multipart.

Выбор технологии потоковой передачи

У каждой технологии потоковой передачи есть свои преимущества и недостатки.

SSE широко поддерживается и является нашей рекомендацией по умолчанию для потоковой передачи ответов.

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

Несмотря на то что многочастный формат очень старый, он поддерживается серверными платформами не так широко. Однако мы считаем, что он лучше соответствует семантике HTTP. Мы рекомендуем его пуристам, готовым немного поработать на стороне сервера, чтобы упростить использование ответов в этом формате.

Клиентские скрипты

htmx поощряет гипермедийный подход к созданию веб-приложений, при котором запросы к серверам обновляют содержимое с помощью HTML-ответов.

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

htmx предлагает различные инструменты, которые упрощают эту задачу:

  • Богатый набор генерируемых событий
  • API для работы с объектом htmx
  • Атрибуты hx-on для базовых встроенных скриптов
  • Поддержка Alpine.js для более сложных встроенных скриптов
  • Расширение hx-live — альтернатива Alpine.js, ориентированная на DOM (новинка htmx 4)

События

В htmx есть обширный набор событий, которые можно отслеживать для регистрации или изменения поведения:

document.body.addEventListener('htmx:after:init', function (evt) {
    setUpElement(evt.detail.elt);
});

Здесь мы используем обычный JavaScript, чтобы отследить инициализацию элемента в htmx и применить к нему дополнительную логику с помощью собственной функции setUpElement().

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

API объекта htmx

Глобальный объект JavaScript htmx предоставляет следующие методы:

Метод Описание
htmx.ajax() отправляет запрос htmx из JavaScript
htmx.find() находит первый элемент, соответствующий CSS-селектору
htmx.findAll() находит все элементы, соответствующие CSS-селектору
htmx.process() инициализирует атрибуты htmx у элемента и его потомков
htmx.swap() запускает жизненный цикл замены без отправки запроса
htmx.initialize() вручную инициализирует htmx, если автоматический запуск происходит слишком рано
htmx.on() добавляет обработчик события
htmx.onLoad() вызывает функцию обратного вызова при обработке htmx нового содержимого
htmx.trigger() создаёт пользовательское событие на элементе
htmx.registerExtension() регистрирует расширение htmx
htmx.parseInterval() преобразует строку времени, например 5s, в миллисекунды
htmx.timeout() создаёт промис, который выполняется по истечении заданного интервала времени

Объект htmx также содержит htmx.config, задающий глобальную конфигурацию.

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

Атрибуты hx-on:*

Можно встраивать обработчики событий JavaScript непосредственно в элементы с помощью синтаксиса hx-on:<event name>:

<button hx-on:click="alert('You clicked me!'); await timeout(1000); console.log('done')">
    Click Me!
</button>

В атрибутах hx-on доступны следующие символы верхнего уровня:

Символ Описание
this элемент, содержащий атрибут hx-on
event событие, вызвавшее обработчик
сведения о событии каждое свойство event.detail доступно напрямую. Для событий htmx сюда входит ctx — контекст запроса
методы htmx каждый метод объекта htmx доступен без префикса htmx. (например, find()

Обратите внимание: эта функция требует eval() и поэтому может не работать при строгой CSP.

Поддержка Alpine.js

Alpine.js — очень популярная библиотека JavaScript, которая значительно расширяет выразительные возможности встроенных скриптов:

  • Обработчики событий с помощью x-on
  • Реактивное состояние с помощью x-data
  • Условное содержимое с помощью x-show и x-if
  • Двусторонняя привязка форм с помощью x-model
  • Вспомогательные функции, такие как $refs, $store, $dispatch и $watch

Alpine — очень популярная библиотека среди разработчиков, использующих htmx. Эти две технологии отлично работают вместе без дополнительной настройки, но расширение hx-alpine-compat помогает устранить некоторые особенности интеграции этих библиотек.

hx-live

hx-live — появившийся в htmx 4 наш подход к реактивным скриптам для веба, ориентированным на DOM. Он вдохновлён Alpine, jQuery и hyperscript.

hx-live предоставляет функцию выбора элементов q(), похожую на jQuery, которая позволяет выбрать один или несколько элементов и обновлять/изменять их как группу. Эта функция доступна на верхнем уровне в атрибутах hx-on: и поддерживает относительные селекторы.

<input placeholder="Enter your name" type="text">
<button hx-on:click="this.text = 'Hello, ' + q('previous input').value"></button>

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

hx-live также поддерживает реактивность на основе DOM:

<input placeholder="Enter your name" type="text">
<p :text="'Hello, ' + q('previous input').value"></p>

В этом случае абзац будет обновляться по мере ввода текста в поле.

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

Другие сторонние библиотеки JavaScript

htmx разработан для эффективной интеграции с большинством сторонних библиотек JavaScript.

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

Хороший пример — демонстрация SortableJS:

<form class="sortable" hx-post="/items" hx-trigger="end">
    <div class="htmx-indicator">Updating...</div>
    <div><input type='hidden' name='item' value='1'/>Item 1</div>
    <div><input type='hidden' name='item' value='2'/>Item 2</div>
    <div><input type='hidden' name='item' value='2'/>Item 3</div>
</form>

Как и в случае с большинством библиотек JavaScript, при использовании Sortable нужно в какой-то момент инициализировать содержимое.

В htmx удобнее всего сделать это с помощью метода htmx.onLoad(), зарегистрировав функцию обратного вызова.

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

htmx.onLoad((content) => {
    var sortables = content.querySelectorAll(".sortable");
    for (var i = 0; i < sortables.length; i++) {
        var sortable = sortables[i];
        new Sortable(sortable, {
            animation: 150,
            ghostClass: 'blue-background-class'
        });
    }
})

Это гарантирует, что сортируемые элементы будут правильно инициализированы при добавлении нового содержимого в DOM с помощью htmx.

Hyperscript

Экспериментальный язык скриптов hyperscript — родственный проект htmx, который без проблем интегрируется с ним.

Подходит не всем, но это довольно забавный небольшой язык:

  <button _="on click add .highlight to <p/> in me">

Веб-компоненты

Обратите внимание: htmx не инициализирует содержимое внутри веб-компонентов автоматически. Необходимо инициализировать его вручную, вызвав htmx.process в методе connectedCallback():

customElements.define('my-counter', class extends HTMLElement {
    connectedCallback() {
        const shadow = this.attachShadow({mode: 'open'})
        shadow.innerHTML = `
          <button hx-post="/increment" hx-target="#count">+1</button>
          <div id="count">0</div>
        `
        htmx.process(shadow) // Initialize htmx for this shadow DOM
    }
})

Обратите внимание: это справедливо независимо от того, использует ли компонент Shadow DOM.

Выбор элементов за пределами Shadow DOM

Если компонент использует Shadow DOM, селекторы вроде hx-target будут находить элементы только внутри той же области Shadow DOM.

Чтобы выйти за пределы Shadow DOM компонента и выбрать сам веб-компонент, можно использовать host в качестве целевого элемента:

<!-- Inside a Web Component -->
<button hx-get="..." hx-target="host">
  ...
</button>

Чтобы выйти за пределы Shadow DOM и выбрать элемент в общей области DOM, можно использовать ключевое слово global, за которым следует пробел и селектор:

<!-- Inside a Web Component -->
<button hx-get="..." hx-target="global #target">
  ...
</button>

Расширения

htmx поддерживает расширения, дополняющие его базовую гипермедийную инфраструктуру.

В комплект htmx входят следующие расширения:

Расширение Категория Описание
hx-multipart Потоковая передача HTML Потоковая передача HTML с помощью multipart/mixed
hx-sse Потоковая передача HTML Потоковая передача HTML с помощью text/event-stream (SSE)
hx-ws Потоковая передача HTML Потоковая передача HTML и отправка данных через веб-сокеты
hx-browser-indicator Пользовательский интерфейс Отображение индикатора загрузки на вкладке с помощью hx-browser-indicator
hx-live Пользовательский интерфейс Наша собственная система реактивных скриптов на основе DOM
hx-pending Пользовательский интерфейс Отображение пользовательского содержимого во время выполнения запросов
hx-prompt Пользовательский интерфейс Запрос подтверждения перед отправкой запросов с помощью hx-prompt='Reason?'
hx-preload Производительность Предварительная загрузка при наведении с помощью hx-preload='mouseover'
hx-history-cache Производительность Восстановление страниц при переходе назад/вперёд из sessionStorage
hx-ptag Производительность Пропуск неизменившихся опросов с помощью HX-PTag: "v42"
hx-download Замена содержимого Загрузка файлов с помощью hx-swap='download'
hx-head Замена содержимого Объединение тегов <head> с помощью hx-head='merge'
hx-targets Замена содержимого Выбор нескольких элементов с помощью hx-targets='.selector'
hx-upsert Замена содержимого Обновление или вставка элементов с помощью hx-swap='upsert'
htmx-2-compat Совместимость Восстановление значений по умолчанию и имён событий htmx 2.x в htmx 4
hx-alpine-compat Совместимость Совместная работа htmx и Alpine.js без конфликтов
hx-csp Безопасность Обеспечение работы htmx при строгой политике безопасности содержимого

Обратите внимание: многие из этих расширений уже включены в htmax.js и поставляются одним файлом.

Использование расширений

Чтобы установить расширение, подключите его скрипт после подключения htmx.

<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/htmx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0/dist/ext/hx-sse.js"></script>

Или с помощью сборщика:

import 'htmx.org';
import 'htmx.org/dist/ext/hx-sse';

Создание расширений

Если вы хотите создать собственное расширение htmx, см. руководство по созданию расширений htmx.

Вопросы безопасности

htmx позволяет определять логику непосредственно в DOM. Это даёт ряд преимуществ, главное из которых — локальность поведения, благодаря которой систему проще понимать и поддерживать.

Однако такой подход вызывает опасения в отношении безопасности: поскольку htmx расширяет выразительные возможности HTML, злоумышленник, способный внедрить HTML в ваше приложение, может использовать эти возможности htmx во вред.

Правило 1: экранируйте всё содержимое пользователя

Первое правило веб-разработки на основе HTML всегда было таким: не доверяйте пользовательскому вводу. Экранируйте всё стороннее и недоверенное содержимое, внедряемое на ваш сайт. Это необходимо, помимо прочего, для предотвращения атак XSS.

На превосходном сайте OWASP есть подробная документация по XSS и способам её предотвращения, включая шпаргалку по предотвращению межсайтового скриптинга.

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

Тем не менее иногда разработчики предпочитают более рискованный способ внедрения HTML, часто используя в шаблонизаторе механизм raw(). Для этого могут быть веские причины, но если внедряемое содержимое поступает от третьей стороны, его необходимо очищать, в том числе удалять атрибуты, начинающиеся с hx- и data-hx, а также встроенные теги <script> и т. д.

Если вы внедряете необработанный HTML и самостоятельно экранируете его, рекомендуется использовать список разрешённых атрибутов и тегов, а не список запрещённых.

Инструменты безопасности htmx

Разумеется, ошибки случаются, а разработчики не идеальны, поэтому для защиты веб-приложения стоит использовать многоуровневый подход. htmx также предоставляет инструменты, помогающие защитить приложение.

Рассмотрим их.

hx-ignore

Первый инструмент htmx для дополнительной защиты приложения — атрибут hx-ignore. Он предотвращает обработку всех атрибутов htmx у указанного элемента и всех вложенных в него элементов. Например, если вы добавляете в шаблон необработанный HTML (ещё раз: это не рекомендуется!), можно обернуть содержимое в div с атрибутом hx-ignore:

<div hx-ignore>
    <%= raw(user_content) %>
</div>

В этом случае htmx не будет обрабатывать атрибуты и функции, связанные с htmx, в этом содержимом. Этот атрибут нельзя отключить внедрением дополнительного содержимого: если атрибут hx-ignore найден где-либо в иерархии предков элемента, htmx не будет его обрабатывать.

Параметры CSP

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

CSP можно задать с помощью HTTP-заголовка или тега <meta>. Предпочтительнее использовать HTTP-заголовки: теги <meta> не обеспечивают соблюдение всех директив, а скрипты, расположенные в документе перед тегом <meta>, не подпадают под действие политики:

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-<nonce>'

Подробное обсуждение CSP выходит за рамки этого документа, но статья MDN — хорошая отправная точка для изучения этой темы.

Управление междомéнными запросами

По умолчанию htmx задаёт для htmx.config.mode значение "same-origin", из-за чего браузер отклоняет любые междоменные запросы fetch, даже если злоумышленник внедрит hx-get, указывающий на другой адрес.

Это правило применяется и к разметке: любое значение mode в атрибуте hx-config отдельного элемента игнорируется и заменяется значением глобальной конфигурации. Внедрённая разметка, например hx-config='{"mode":"cors"}', не может расширить область запросов.

Если вашему приложению действительно нужен CORS (например, для API на другом поддомене):

  1. Задайте режим глобально:
    htmx.config.mode = "cors";
  2. Ограничьте доступные источники с помощью connect-src:
    <meta http-equiv="Content-Security-Policy"
          content="connect-src 'self' https://api.example.com">

При наличии обеих настроек htmx сможет обращаться к вашему API, но CSP заблокирует внедрённые целевые URL-адреса, ведущие к другим источникам.

Расширение hx-csp

Для сайтов, использующих nonce в CSP для скриптов, расширение hx-csp обеспечивает глубокую интеграцию:

  • Обработка всех атрибутов htmx разрешается только при наличии nonce для текущего запроса, что блокирует внедрённые атрибуты htmx
  • Автоматически создаёт политику Trusted Types 'htmx', благодаря которой только htmx может записывать HTML в DOM-приёмники
  • Заменяет new Function() eval на внедрение скрипта с nonce, если задан safeEval:true, устраняя необходимость в unsafe-eval

Полные инструкции по настройке см. в документации расширения hx-csp.

htmx и eval

Для некоторых необязательных функций htmx использует new Function():

  • Фильтры событий
  • Атрибут hx-on
  • Значения атрибутов, начинающиеся с js: или javascript:

Все эти функции необязательны. Если вы ими не пользуетесь, директиву unsafe-eval можно полностью исключить из CSP.

Если вы используете эти функции, расширение hx-csp с safeEval:true заменяет new Function() внедрением скрипта с nonce, позволяя использовать их без unsafe-eval.

CSP и встроенные стили

Для внедрения CSS-индикатора htmx использует конструируемые таблицы стилей (document.adoptedStyleSheets), на которые не распространяются ограничения CSP style-src.

Следует учитывать замену слиянием (morph swap), применяемую вместе с JS-фреймворками, такими как Alpine, которые задают атрибуты style с помощью JavaScript. Во время слияния htmx согласует атрибуты старого и нового элементов, включая любое значение style. При строгой политике style-src без 'unsafe-inline' этот вызов setAttribute("style", ...) вызовет нарушение CSP.

Добавьте "style" в morphIgnore, чтобы пропустить его:

<meta name="htmx-config" content='{"morphIgnore":["data-htmx-powered","style"]}'>

Переходы CSS на основе классов продолжат работать как обычно.

Защита от CSRF

Назначение и проверка CSRF-токенов обычно относятся к обязанностям серверной части, но htmx может автоматически добавлять CSRF-токен к каждому запросу с помощью атрибута hx-headers. Этот атрибут необходимо добавить к элементу, отправляющему запрос, или к одному из его родительских элементов. Поэтому элементы html и body удобно использовать как глобальные контейнеры для добавления CSRF-токена в заголовок запроса HTTP, как показано ниже.

<html lang="en" hx-headers:inherited='{"X-CSRF-TOKEN": "CSRF_TOKEN_INSERTED_HERE"}'>
:
</html>

Перечисленные выше элементы обычно уникальны в HTML-документе, поэтому их должно быть легко найти в шаблонах.

Кэширование

htmx из коробки работает со стандартными механизмами кэширования HTTP.

Если сервер добавляет HTTP-заголовок ответа Last-Modified для определённого URL, браузер автоматически добавит HTTP-заголовок запроса If-Modified-Since к следующим запросам к тому же URL.

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

Помните: если сервер может формировать разное содержимое для одного URL в зависимости от других заголовков, необходимо использовать HTTP-заголовок ответа Vary.

Например, если сервер возвращает полный HTML, когда заголовок HX-Request отсутствует или имеет значение false, и возвращает фрагмент этого HTML, когда HX-Request: true, необходимо добавить Vary: HX-Request. Тогда ключ кэша будет учитывать сочетание URL ответа и заголовка запроса HX-Request, а не только URL ответа.

Отладка

Декларативное и событийно-ориентированное программирование с htmx (или любым другим декларативным языком) может быть увлекательным и очень продуктивным, но по сравнению с императивными подходами у него есть недостаток: отлаживать его может быть сложнее.

Например, если вы не знаете нужных приёмов, бывает трудно понять, почему что-то не происходит.

Вот несколько советов:

По умолчанию ошибки и предупреждения выводятся в console.error / console.warn. Чтобы также видеть все события, генерируемые htmx, задайте htmx.config.logAll = true:

htmx.config.logAll = true;

Инструменты наблюдаемости (Sentry, DataDog RUM, LogRocket и т. д.) автоматически перехватывают console.*, поэтому журналы htmx попадают в вашу существующую систему без дополнительной настройки.

Разумеется, это не объяснит, почему htmx не выполняет какое-либо действие. Возможно, вы также не знаете, какие события генерирует элемент DOM и можно ли использовать их для запуска. Для этого можно воспользоваться методом monitorEvents() в консоли браузера:

monitorEvents(htmx.find("#theElement"));

В консоль будут выводиться все события элемента с идентификатором theElement, чтобы вы могли точно увидеть, что с ним происходит.

Обратите внимание: этот способ работает только в консоли; встроить его в тег script на странице нельзя.

И наконец, если ничего не помогает, попробуйте отлаживать htmx.js, загрузив неминифицированную версию.

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

Если вам нужна помощь, всегда можно зайти на сервер Discord.

Поддержка редакторов

htmx достаточно прост, и поддержка редактора не обязательна для его использования. Информацию об инструментах для предпочитаемого вами редактора см. в разделе «Поддержка редакторов».

Конфигурация

В htmx есть параметры конфигурации, доступные как программно, так и декларативно.

Они перечислены ниже:

Переменная конфигурации Описание
htmx.config.logAll по умолчанию false; если задано значение true, htmx будет записывать все события в консоль для отладки
htmx.config.prefix по умолчанию "data-hx-"; дополнительный префикс атрибутов, распознаваемый наряду с всегда активным префиксом hx- (например, data-hx-get работает по умолчанию). Установите значение "", чтобы отключить. Должен быть задан через метатег; установка этого параметра после загрузки страницы не даст нужного результата.
htmx.config.transitions по умолчанию false; использовать ли переходы представлений при замене содержимого (если браузер поддерживает их)
htmx.config.history по умолчанию true; включена ли поддержка истории. Установите значение "reload", чтобы при переходе по истории полностью перезагружать страницу вместо выполнения AJAX-запроса
htmx.config.mode по умолчанию 'same-origin'; режим fetch для AJAX-запросов. Возможные значения: 'cors', 'no-cors' или 'same-origin'
htmx.config.defaultSwap по умолчанию innerHTML
htmx.config.indicatorClass по умолчанию htmx-indicator
htmx.config.requestClass по умолчанию htmx-request
htmx.config.includeIndicatorCSS по умолчанию true (определяет, загружаются ли стили индикатора)
htmx.config.defaultTimeout по умолчанию 60000 (60 секунд); количество миллисекунд, по истечении которого запрос автоматически прерывается
htmx.config.inlineScriptNonce по умолчанию не задано; это означает, что к встроенным скриптам не будет добавлен nonce
htmx.config.extensions по умолчанию ''; список имён расширений, разделённых запятыми, для загрузки (например, 'preload,pending')
htmx.config.morphIgnore по умолчанию ["data-htmx-powered"]; массив префиксов имён атрибутов, которые нужно сохранять при морфинге элементов
htmx.config.morphScanLimit ограничивает количество узлов, проверяемых во время морфинга
htmx.config.morphSkip по умолчанию '[hx-morph-skip]'; CSS-селектор элементов, которые нужно полностью пропускать при морфинге (они остаются неизменными)
htmx.config.morphSkipChildren по умолчанию '[hx-morph-skip-children]'; CSS-селектор элементов, атрибуты которых обновляются, а дочерние элементы сохраняются при морфинге
htmx.config.noSwap по умолчанию [204, 304]; массив кодов состояния HTTP, при которых не следует выполнять замену
htmx.config.allowEmptySwapAfterOOB по умолчанию false; выполнять ли основную замену, если ответ содержал только внеполосные элементы
htmx.config.implicitInheritance по умолчанию false; если задано значение true, атрибуты будут автоматически наследоваться от родительских элементов, и указывать модификатор :inherited не потребуется
htmx.config.defaultFocusScroll по умолчанию false; прокручивать ли страницу до сфокусированных элементов после замены
htmx.config.defaultSettleDelay по умолчанию 1 (мс); задержка между этапами замены и фиксации
htmx.config.metaCharacter по умолчанию undefined; позволяет использовать для модификаторов атрибутов пользовательский символ вместо : (например, -, чтобы использовать hx-get-inherited вместо hx-get:inherited)

Большинство параметров можно задать непосредственно в JavaScript или с помощью тега meta (принимает HCON или JSON):

Примечание: Некоторые параметры считываются только один раз при инициализации, поэтому для их применения их необходимо задать с помощью тега meta. К ним относятся prefix, extensions и metaCharacter.

<meta name="htmx-config" content='{"defaultSwap":"innerHTML"}'>

Заключение

Вот и всё!

Приятной работы с htmx!

С помощью многого можно справиться, не написав много кода!

Licensed under the Zero-Clause BSD License.
https://four.htmx.org/docs/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API