Введение в htmx, библиотеку динамического пользовательского интерфейса, ориентированную на HTML

Введение в htmx, библиотеку динамического пользовательского Изучение

Сегодня веб-пользователи ожидают плавного и динамичного взаимодействия с одностраничными приложениями (SPA). Однако при создании SPA часто используются сложные фреймворки, такие как React и Angular, которые могут быть сложными для изучения и работы. Откройте для себя htmx — библиотеку, которая предлагает свежий взгляд на создание динамичного веб-интерфейса за счет использования таких функций, как переходы Ajax и CSS непосредственно в HTML.

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

Что такое htmx и как он работает?

При создании интерактивных веб-приложений у разработчиков традиционно было два основных варианта, каждый со своими компромиссами. С одной стороны, существуют многостраничные приложения (MPA), которые обновляют всю страницу каждый раз, когда пользователь взаимодействует с ней. Такой подход гарантирует, что сервер контролирует состояние приложения, а клиент точно представляет его. Однако полная перезагрузка страницы может привести к медленной и неуклюжей работе пользователя.

С другой стороны, существуют одностраничные приложения (SPA), которые полагаются на JavaScript, работающий в браузере, для управления состоянием приложения. Они взаимодействуют с сервером с помощью вызовов API, которые возвращают данные, часто в формате JSON. Затем SPA использует эти данные для обновления пользовательского интерфейса без обновления страницы, обеспечивая гораздо более плавный пользовательский интерфейс, что-то вроде родного настольного или мобильного приложения. Однако и этот подход не идеален. Вычислительные накладные расходы обычно выше из-за существенной обработки на стороне клиента, начальное время загрузки может быть медленнее, поскольку клиенту приходится загружать и анализировать большие пакеты JavaScript перед рендерингом первой страницы, а настройка среды разработки часто связана со сложной сборкой. инструменты и рабочие процессы.

Читайте также:  Тенденции цифровой трансформации здравоохранения в 2022 году

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

Этот подход упрощает процесс разработки за счет минимизации сложности на стороне клиента, а также значительного использования JavaScript, характерного для SPA. Он не требует сложной настройки и обеспечивает плавный и отзывчивый пользовательский интерфейс.

Установка htmlx

Есть несколько способов включить htmx в ваш проект. Вы можете загрузить его прямо со страницы проекта на GitHub или, если вы работаете с Node.js, установить его через npm с помощью команды npm install htmx.org.

Однако самый простой способ, который мы будем использовать в этом руководстве, — включить его через сеть доставки контента (CDN). Это позволяет нам начать использовать htmx без какой-либо настройки или процесса установки. Просто включите следующий тег скрипта в свой HTML-файл:

<script src="https://unpkg.com/htmx.org@1.9.4"></script>

Этот тег сценария указывает на версию 1.9.4, но вы можете заменить «1.9.4» последней версией, если она доступна.

htmx очень легкий, его уменьшенная и сжатая версия весит около 14 КБ. Он не имеет зависимостей и совместим со всеми основными браузерами, включая IE11.

После того, как вы добавили htmx в свой проект, вы можете проверить, правильно ли он работает. Вы можете проверить это на следующем простом примере:

<button
  hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode&type=single"
  hx-target="#joke-container"
>
  Make me laugh!
</button>

<p id="joke-container">Click the button to load a joke...</p>

Когда вы нажимаете кнопку, если htmx работает правильно, он отправит запрос GET к Joke API и заменит содержимое тега <p>ответом сервера.

Когда вы нажимаете кнопку, если htmx работает прав

Ajax-запросы: подход htmx

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

  • hx-get: отправляет запрос GET на указанный URL.
  • hx-post: отправляет запрос POST на указанный URL.
  • hx-put: отправляет запрос PUT на определенный URL.
  • hx-patch: отправляет запрос PATCH на заданный URL.
  • hx-delete: отправляет запрос DELETE на объявленный URL.

Эти атрибуты принимают URL-адрес, на который они будут отправлять запрос Ajax. По умолчанию запросы Ajax запускаются «естественным» событием HTML-элемента (например, щелчком в случае кнопки или событием изменения в случае поля ввода).

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

<button hx-get="/api/resource">Load Data</button>

В приведенном выше примере элементу buttonназначается hx-getатрибут. Как только кнопка нажата, GET-запрос отправляется на /api/resourceURL-адрес.

Что происходит, когда данные возвращаются с сервера? По умолчанию htmx вставит этот ответ непосредственно в инициирующий элемент — в нашем примере это файл button. Однако htmx не ограничивается этим поведением и предоставляет возможность указывать различные элементы в качестве места назначения для данных ответа. Мы подробнее рассмотрим эту возможность в следующих разделах.

Запуск запросов с помощью htmx

htmx инициирует запрос Ajax в ответ на определенные события, происходящие с определенными элементами:

  • Для элементов и inputэто событие.textareaselectchange
  • Для formэлементов это submitсобытие.
  • Для всех остальных элементов это событие click.

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

<label>Keyword:
  <input
    type="text"
    placeholder="Enter a keyword..."
    hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode"
    hx-target="#joke-container"
    name="contains"
  />
</label>

<p id="joke-container">Results will appear here</p>

Чтобы запустить поиск, нам нужно запустить событие изменения. Для <input>элементов это происходит, когда элемент теряет фокус после изменения его значения. Поэтому введите что-нибудь в поле (например, «бар»), щелкните в другом месте страницы, и в элементе должна появиться шутка <div>.

Это хорошо, но обычно пользователи ожидают, что их результаты поиска будут обновляться по мере их ввода. Для этого мы можем добавить triggerк нашему <input>элементу атрибут htmx:

<input
  ...
  hx-trigger="keyup"
/>

Теперь результаты обновляются немедленно. Это хорошо, но возникает новая проблема: теперь мы делаем вызов API при каждом нажатии клавиши. Чтобы избежать этого, мы можем использовать модификатор для изменения поведения триггера. htx предлагает следующее:

  • once: используйте этот модификатор, если хотите, чтобы запрос выполнялся только один раз.
  • changed: этот модификатор гарантирует, что запрос будет выдан только в том случае, если значение элемента было изменено.
  • delay:<time interval>: этот модификатор устанавливает период ожидания (например 1s, ) перед отправкой запроса. Если событие снова сработает в течение этого периода ожидания, обратный отсчет сбрасывается.
  • throttle:<time interval>: с помощью этого модификатора вы также можете установить период ожидания (например, 1s) перед отправкой запроса. Однако, в отличие от delay, если новое событие инициируется в течение установленного времени, это событие будет проигнорировано, гарантируя, что запрос будет инициирован только после определенного периода.
  • from:<CSS Selector>: этот модификатор позволяет прослушивать событие на отдельном элементе, а не на исходном.

В данном случае кажется, что это delayто, что нам нужно:

<input
  ...
  hx-trigger="keyup delay:500ms"
/>

И теперь, когда вы вводите в поле (попробуйте более длинное слово, например, «разработчик»), запрос запускается только тогда, когда вы делаете паузу или заканчиваете ввод.

Как видите, это позволяет нам реализовать шаблон активного поля поиска всего в нескольких строках клиентского кода.

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

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

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

Когда htmx делает запрос Ajax, он применяет htmx-requestкласс к инициирующему элементу. Класс htmx-requestзаставит этот — или любой дочерний элемент с htmx-indicatorклассом — перейти к непрозрачности, равной 1.

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

<button hx-get="/api/data">
  Load data
  <img class="htmx-indicator" src="/spinner.gif" alt="Loading spinner">
</button>

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

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

Давайте продемонстрируем это на нашем примере Joke API:

<input
  ...
  hx-indicator=".loader"
/>

<span class="loader htmx-indicator"></span>

Примечание: мы можем получить некоторые стили CSS для счетчика из CSS Loaders & Spinners. Есть из чего выбирать; просто нажмите один, чтобы получить HTML и CSS.

Это приведет к отображению счетчика загрузки, пока запрос находится в полете.

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

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

function sleep(milliseconds) {
  const date = Date.now();
  let currentDate = null;
  do {
    currentDate = Date.now();
  } while (currentDate - date < milliseconds);
}

document.body.addEventListener('htmx:afterOnLoad',  () => {
  sleep(2000);
});

При этом используется система событий htmx, которую мы можем использовать для изменения и улучшения ее поведения. Здесь мы используем событие, которое запускается после завершения htmx:afterOnLoadAjax. onloadЯ также использую функцию сна из статьи SitePoint на ту же тему.

Вот завершенная демонстрация. Введите что-нибудь в поле (например, «JavaScript»), а затем наблюдайте за индикатором загрузки после того, как запрос будет инициирован.

Ориентация на элементы и обмен контентом

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

Мы действительно видели это в нашем первом примере:

<button
  hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode&type=single"
  hx-target="#joke-container"
>
  Make me laugh!
</button>

Вместо того, чтобы кнопка заменяла свое собственное содержимое, hx-targetатрибут указывает, что ответ должен заменить содержимое элемента идентификатором «joke-container».

Расширенные селекторы CSS

htmx также предлагает несколько более продвинутых способов выбора элементов, в которые следует загрузить содержимое. К ним относятся this, closest, next, previousи find.

  • Ключевое thisслово указывает, что элемент с hx-targetатрибутом является фактической целью.
  • Ключевое closestслово находит ближайшего предка исходного элемента, который соответствует заданному селектору CSS.
  • Ключевые слова nextи previousнаходят следующий или предыдущий элемент в модели DOM, который соответствует заданному селектору CSS.
  • Ключевое findслово находит первый дочерний элемент, который соответствует заданному селектору CSS.

Ссылаясь на наш предыдущий пример, мы могли бы также написать hx-target=»next p», чтобы не указывать идентификатор.

Замена контента

По умолчанию htmx заменяет содержимое целевого элемента ответом Ajax. Но что, если мы хотим добавить новый контент, а не заменить его? Вот тут-то и hx-swapпоявляется атрибут. Этот атрибут позволяет нам указать, как новое содержимое должно быть вставлено в целевой элемент. Возможные значения: outerHTML, innerHTML, beforebegin, afterbegin, beforeend, и afterend. Использование hx-swap=»beforeend», например, добавило бы новое содержимое в конец целевого элемента, что было бы идеально для нашего нового сценария комментариев.

CSS-переходы с помощью htmx

Переходы CSS позволяют плавно изменять стиль элемента из одного состояния в другое без использования JavaScript. Эти переходы могут быть такими же простыми, как изменение цвета, или такими сложными, как полное трехмерное преобразование.

htmx позволяет легко использовать CSS-переходы в нашем коде: все, что нам нужно сделать, это поддерживать согласованный идентификатор элемента в HTTP-запросах.

Рассмотрим этот HTML-контент:

<button hx-get="/new-content" hx-target="#content">
  Fetch Data
</button>

<div id="content">
  Initial Content
</div>

После запроса htmx Ajax /new-contentсервер возвращает это:

<div id="content" class="fadeIn">
  New Content
</div>

Несмотря на изменение содержания, <div>идентификатор сохраняется. Однако fadeInк новому содержимому был добавлен класс.

Теперь мы можем создать переход CSS, который плавно переходит из исходного состояния в новое состояние:

.fadeIn {
  animation: fadeIn 2.5s;
}

@keyframes fadeIn {
  0% {opacity: 0;}
  100% {opacity: 1;}
}

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

Использование API просмотра переходов

Новый View Transitions API предоставляет способ анимации между различными состояниями элемента DOM. В отличие от переходов CSS, которые включают в себя изменения свойств CSS элемента, переходы представления связаны с анимацией изменений содержимого элемента.

View Transitions API — это новая экспериментальная функция, которая в настоящее время находится в активной разработке. На момент написания этой статьи этот API реализован в Chrome 111+, и ожидается, что в будущем будет добавлена ​​поддержка других браузеров (вы можете проверить его поддержку на caniuse ). htmx предоставляет интерфейс для работы с View Transitions API и использует механизм отсутствия перехода в браузерах, где API недоступен.

В htmx есть несколько способов использования View Transitions API:

  • Установите htmx.config.globalViewTransitionsпеременную конфигурации в true. Это будет использовать переходы для всех свопов.
  • Используйте transition:trueопцию в hx-swapатрибуте.

Переходы просмотра можно определить и настроить с помощью CSS. Вот пример перехода «отскок», когда старый контент отскакивает, а новый возвращается:

@keyframes bounce-in {
  0% { transform: scale(0.1); opacity: 0; }
  60% { transform: scale(1.2); opacity: 1; }
  100% { transform: scale(1); }
}

@keyframes bounce-out {
  0% { transform: scale(1); }
  45% { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(0); opacity: 0; }
}

.bounce-it {
  view-transition-name: bounce-it;
}

::view-transition-old(bounce-it) {
  animation: 600ms cubic-bezier(0.4, 0, 0.2, 1) both bounce-out;
}

::view-transition-new(bounce-it) {
  animation: 600ms cubic-bezier(0.4, 0, 0.2, 1) both bounce-in;
}

В коде htmx мы используем transition:trueпараметр в hx-swapатрибуте и применяем bounce-itкласс к содержимому, которое мы хотим анимировать:

<button 
  hx-get="https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode" 
  hx-swap="innerHTML transition:true" 
  hx-target="#joke-container"
>
  Load new joke
</button>

<div id="joke-container" class="bounce-it">
  <p>Initial joke content goes here...</p>
</div>

В этом примере, когда <div>содержимое обновляется, старое содержимое будет отражаться, а новое — появляться, создавая приятный и привлекательный визуальный эффект.

Имейте в виду, что в настоящее время эта демонстрация работает только в браузерах на базе Chromium.

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

htmx хорошо интегрируется с HTML5 Validation API и предотвращает отправку запросов форм, если пользовательский ввод не проходит проверку.

Например, когда пользователь нажимает «Отправить», запрос POST будет отправлен только в /contactтом случае, если поле ввода содержит действительный адрес электронной почты:

<form hx-post="/contact">
  <label>Email:
    <input type="email" name="email" required>
  </label>
  <button>Submit</button>
</form>

Если бы мы хотели сделать еще один шаг, мы могли бы добавить некоторую проверку сервера, чтобы гарантировать, что gmail.comпринимаются только адреса:

<form hx-post="/contact">
  <div hx-target="this" hx-swap="outerHTML">
    <label>Email:
      <input type="email" name="email" required hx-post="/contact/email">
    </label>
  </div>
  <button>Submit</button>
</form>

Здесь мы добавили родительский элемент ( div#wrapper), который объявляет себя получателем запроса (используя thisключевое слово) и использует outerHTMLстратегию обмена. Это означает, что все <div>будет заменено ответом сервера, даже если это не фактический элемент, инициирующий запрос.

Мы также добавили hx-post=»/contact/email»в поле ввода, что означает, что всякий раз, когда это поле размыто, оно будет отправлять запрос POST на /contact/emailконечную точку. Этот запрос будет содержать значение нашего поля.

На сервере (в /contact/email) мы могли бы выполнить проверку с помощью PHP:

<?php
// Get the email from the form submission
$email = $_POST['email'];

// Validate that the domain is "gmail.com" using regex
$pattern = "/@gmail\.com$/i"; // Case-insensitive match for "@gmail.com" at the end of the email address
$error = !preg_match($pattern, $email);

// Sanitize email to avoid XXS
$sanitizedEmail = htmlspecialchars($email, ENT_QUOTES, 'UTF-8');

// Create error message if there's an error
$errorMessage = $error ? '<div class="error-message">Only Gmail addresses accepted!</div>' : '';

// Construct the HTML template, with conditional error message
$template = <<<EOT
  <div hx-target="this" hx-swap="outerHTML">
    <label>Email:
      <input type="email" name="email" hx-post="/contact/email" value="$sanitizedEmail">
      $errorMessage
    </label>
  </div>
EOT;

// return the template
echo $template;
?>

Как видите, htmx ожидает от сервера ответа HTML ( не JSON), который затем вставляется на страницу в указанном месте.

Если мы запустим приведенный выше код, введем не- gmail.comадрес в поле ввода, а затем потеряем фокус ввода, под полем появится сообщение об ошибке «Принимаются только адреса Gmail!»

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

Также стоит отметить, что htmx запускает набор событий вокруг процесса проверки, которые мы можем использовать для добавления собственной логики проверки и методов обработки ошибок. Например, если бы мы хотели реализовать проверку электронной почты в коде JavaScript, мы могли бы сделать это:

<form hx-post="/contact">
  <label>Email:
    <input type="email" name="email" required>
  </label>
  <button>Submit</button>
</form>

<script>
  const emailInput = document.querySelector('input[type="email"]');

  emailInput.addEventListener('htmx:validation:validate', function() {
    const  pattern = /@gmail\.com$/i;

    if (!pattern.test(this.value)) {
      this.setCustomValidity('Only Gmail addresses accepted!');
      this.reportValidity();
    }
  });
</script>

Здесь мы используем событие htmx htmx:validation:validate, которое вызывается перед checkValidity()вызовом метода элементов.

Теперь, когда мы попытаемся отправить форму без gmail.comадреса, мы увидим то же сообщение об ошибке.

Что еще может сделать htmx?

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

Прежде чем мы закончим, давайте кратко рассмотрим некоторые из этих дополнительных возможностей.

Расширения

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

Вы можете найти список доступных расширений на сайте htmx.

Повышение

«Ускоряющая» функциональность htmx позволяет нам улучшать стандартные HTML-якоря и формы, преобразовывая их в Ajax-запросы (сродни технологиям вроде pjax из прошлого):

<div hx-boost="true">
  <a href="/blog">Blog</a>
</div>

Тег привязки в этом div выдаст GETзапрос Ajax /blogи заменит HTML-ответ на <body>тег.

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

Управление историей

Говоря о SPA, htmx также имеет встроенную поддержку управления историей, соответствующую стандартному API истории браузера. Благодаря этому мы можем вставлять URL-адреса в панель навигации браузера и сохранять текущее состояние страницы в истории браузера, гарантируя, что кнопка «Назад» ведет себя так, как ожидают пользователи. Это позволяет нам создавать веб-страницы, которые выглядят как SPA, сохраняя состояние и управляя навигацией без перезагрузки всей страницы.

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

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

Также есть пример подтверждения, который показывает, как использовать sweetalert2 для подтверждения действий htmx (хотя здесь также используется гиперскрипт, экспериментальный язык сценариев внешнего интерфейса, разработанный для того, чтобы быть выразительным и легко встраиваемым непосредственно в HTML).

Заключение

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

Однако это не универсальное решение. Для более сложных приложений вам все еще может понадобиться среда JavaScript. Но если ваша цель — создать быстрое, интерактивное и удобное для пользователя веб-приложение, не усложняя его, определенно стоит рассмотреть htmx.

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

Оцените статью
bestprogrammer.ru
Добавить комментарий