Ajax 通信を簡単にする htmx の基本と実践

htmx は、JavaScript のコードを書かずにサーバーとの非同期通信を実現し、ページの一部を更新することを可能にする JavaScriptライブラリです。HTML属性の拡張により簡単に使用できるようにし、結果として、コードの可読性が向上し、将来のメンテナンスも容易になります。これらの特徴から、htmx はウェブサイト制作の現場での活用が期待されます。


目次:

  1. htmxの主な特徴
  2. htmxの利用方法
  3. htmxのコア属性: hx-get , hx-post
  4. イベントハンドリング: hx-trigger属性
  5. 更新ターゲットの指定: hx-target属性
  6. コンテンツの挿入と置換: hx-swap属性
  7. ケーススタディと実践例
  8. コミュニティとサポート
  9. 最後に

htmxの主な特徴

  1. 簡潔さとアクセシビリティ: htmx は、複雑な JavaScriptコードを書かずに、HTML 内で直接動的な振る舞いを宣言することを可能にします。これにより、Web開発がよりアクセスしやすく、より理解しやすくなることを意味します。
  2. 非同期リクエストの簡易化 : htmx は、Ajaxリクエストを簡単に実装するための属性を提供します。これにより、サーバーへの非同期リクエストを簡単に行い、ページの一部を更新できます。
  3. 導入の容易さ : 既存の Webページやアプリケーションに htmx を追加することは、既存のコードを大幅に変更することなく、容易に行うことができます。これは、htmx が HTMLタグにカスタム属性を追加することによって動作するためです。

htmxの利用方法

htmx を使い始める最も速い方法は、CDN 経由で利用することです。以下のタグをタグに追加するだけで始めることができます。しかし、公式サイトでは、本番環境では CDN の使用を避けるようにとの記述がありますので、注意してください。このページでは、以下のコードを読み込んでテスト的に動かしています。

<script src="https://unpkg.com/htmx.org@1.9.10" 
 integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" 
 crossorigin="anonymous"></script>

ダウンロード:htmx.min.js  ( from unpkg.com )

次に、実際の動作デモ用のコードです。

<button 
 hx-get="/demo/htmx-sample.html" 
 hx-trigger="click" 
 hx-target="#demo" 
 hx-swap="innerHTML"
>htmxデモ</button>
<div id="demo">このコンテンツが変わります</div>

「htmxデモ」と書かれているボタンをクリックする( hx-trigger )と、id="demo"( hx-target )のDIVの内側に( hx-swap )"/demo/htmx-sample.html"( hx-get )の HTML が読み込まれます。

どのように利用するのかはアイデア次第ではありますが、数年後であったり、他人が書いたものであったとしても、button の中を読めば何が起こるのかは想像で分かる記述となっています。

順に hx-属性について説明を続けていきます。

htmx のコア属性 : hx-get属性 , hx-post属性

今回のデモでは、GET のリクエストということで hx-get を利用しましたが、多くの情報を送信する必要がある <form> などでは POST のリクエストを発行する hx-post を利用します。他にも hx-put , hx-patch , hx-delete 属性が用意されており、htmx の核となる HTML から直接 AJAXリクエストを発行できる属性は 5つ用意されています。

これらの属性を使用することで、HTMLタグに簡単に追加するだけで、サーバーとの非同期通信を実現し、ページの再読み込みなしにコンテンツを動的に更新することができます。

イベントハンドリング: hx-trigger属性

今回のデモでは、hx-trigger="click" ということで、ボタンをクリックしたというイベントが発生した時に発動するという指定となっています。

hx-trigger属性は、htmxで非常に便利な機能の一つで、特定のイベントが発生したときに htmxリクエストをトリガー(発動)するために使用されます。この属性を使うことで、ユーザーのアクション(例えば、クリック、マウスオーバー、フォームの送信など)に応じてサーバーに非同期リクエストを送ることができます。これにより、ページの再読み込みなしにデータの更新や取得が可能になり、よりリッチでインタラクティブな UX を提供できます。

hx-trigger属性は、どの DOMイベントがリクエストのトリガーとなるかを指定します。以下にいくつかの使用例を示します。

click : ユーザーが要素をクリックしたときにリクエストを発行するには、hx-trigger="click" を使用します。

<button hx-get="/path/to/resource" hx-trigger="click">クリックしてデータを取得</button>

submit : フォームの送信をトリガーとしてリクエストを発行するには、hx-trigger="submit" を使用します。

<form hx-post="/submit/form" hx-trigger="submit">
    <!-- フォームの内容 -->
    <button type="submit">送信</button>
</form>

change : 入力フィールドやセレクトボックスの値が変更されたときにリクエストを発行します。フォームの入力が変わるたびにサーバーに問い合わせたい場合などに便利です。

<select hx-get="/path/to/resource" hx-trigger="change">
    <option value="1">オプション1</option>
    <option value="2">オプション2</option>
</select>

load : 要素が読み込まれたときに一度だけリクエストを発行します。ページや特定のコンポーネントの初期化に使用します。

<div hx-get="/path/to/content" hx-trigger="load"></div>

mouseover : 要素にマウスカーソルが乗ったときにリクエストを発行するには、hx-trigger="mouseover" を使用します。

<div hx-get="/path/to/info" hx-trigger="mouseover">マウスを乗せて情報を表示</div>

focus : 要素がフォーカスを得たときにリクエストを発行します。テキストフィールドにフォーカスが当たったときに特定のデータをロードしたい場合などに使用します。

<input type="text" hx-get="/path/to/data" hx-trigger="focus">

blur : 要素からフォーカスが外れたときにリクエストを発行します。入力フィールドからフォーカスが外れたときに入力値の検証を行いたい場合に使います。

<input type="text" hx-get="/validate/input" hx-trigger="blur">

keyup : キーボードのキーが離されたときにリクエストを発行します。リアルタイムでの入力値の検証やサジェスト機能の実装に役立ちます。

<input type="text" hx-get="/search" hx-trigger="keyup">

keydown : キーボードのキーが押されたときにリクエストを発行します。特定のキー操作に応じたアクションを実行したい場合に使用します。

mouseenter : マウスポインタが要素の上に入ったときにリクエストを発行します。mouseoverよりも細かい制御が可能です。

mouseleave : マウスポインタが要素から離れたときにリクエストを発行します。要素からマウスが離れたときに何らかのアクションを実行したい場合に便利です。

scroll : 要素がスクロールされたときにリクエストを発行します。スクロールに応じた動的なコンテンツの読み込みなどに使用できます。

resize : ウィンドウや要素のサイズが変更されたときにリクエストを発行します。レスポンシブなデザインの調整や、サイズ変更に応じたコンテンツの更新に役立ちます。

これらのイベント以外にも、カスタムイベントをトリガーとして定義することが可能です。また、複数のイベントをトリガーとして指定することもでき、その場合はイベント名をカンマで区切って指定します。これにより、非常に柔軟なユーザーインタラクションの実現が可能になります。

高度なイベント制御: hx-triggerフィルター

htmxhx-trigger を、特定の条件下でのみ Ajaxリクエストをトリガーするために使用されるフィルター機能が用意されています。これらのフィルターを使用することで、イベントが発生した際にすべてのケースでリクエストを発行するのではなく、特定の条件が満たされた場合にのみリクエストを発行するように制御できます。これにより、より複雑なユーザーインタラクションを実現し、不要なリクエストを減らしてパフォーマンスを向上させることができます。

キーボードイベントのフィルタリング : keydown、keyupなどのキーボードイベントに対して、特定のキーが押されたときのみリクエストをトリガーします。例えば、Enterキーが押されたときのみフォームを送信する場合に便利です。

<input type="text" hx-get="/search" hx-trigger="keyup[enter]">

デバウンスフィルター : 連続したイベント発生を制限し、指定した時間内に最後に発生したイベントのみをトリガーとして扱います。これは、ユーザーがタイピングを終えるまで待ってから検索を行うなど、頻繁に発生するイベントに対してリクエストを制限したい場合に有効です。

<input type="text" hx-get="/search" hx-trigger="keyup changed, debounce:300">

要素の特定の状態でのフィルタリング : チェックボックスがチェックされている時や、特定のオプションが選択されているときのみリクエストを発行するように制御することができます。

<input type="checkbox" hx-get="/filter" hx-trigger="change:checked">

一度だけ : イベントを一度だけトリガーし、その後は無視します。これは、初期化処理など、ページ読み込み時に一度だけ実行したい処理に適しています。

<div hx-get="/init" hx-trigger="load once"></div>

遅延 : イベント発生後、指定したミリ秒の遅延を経てリクエストをトリガーします。ユーザーの入力待ちや、意図的に処理を遅らせたい場合に有用です。

<input type="text" hx-get="/search" hx-trigger="keyup delay:500">

抑制 : 指定した時間内に発生するイベントの中で最初の一回のみをトリガーします。これは、高頻度で発生するイベント(例えばスクロールやリサイズ)の処理を適度に制限し、パフォーマンスの低下を防ぎたい場合に役立ちます。

<div hx-get="/update" hx-trigger="scroll throttle:200"></div>

定期的な実行 : 指定した間隔で定期的にリクエストをトリガーします。リアルタイムデータの更新や、定期的な状態確認などに使用されます。

<div hx-get="/refresh" hx-trigger="every:1000"></div>

※ 上記の時間の指定についての単位はミリ秒になりますので 1000 で 1秒になります。

更新ターゲットの指定: hx-target属性

htmx を使用して Webページを部分的に更新する際に、サーバーからの応答をどの HTML要素に挿入するかを指定するために使用されます。

hx-target="this" : hx-target属性が設定されている要素自身に挿入します。
hx-target="#elementId" : 指定されたIDを持つ要素に挿入します。
hx-target=".className" : 指定されたクラス名を持つ要素に挿入します。

今回のデモでは、hx-target="#demo" と指定いることから id=”demo” を持つ要素に対して指定しています。もし、hx-target の指定が無い場合には this が指定された事になります。

コンテンツの挿入と置換: hx-swap属性

htmx を使用し、サーバーからの応答を受け取った後に、その応答をどのように現在のページに挿入(または置換)するかを指定するために使用されます。hx-swap属性によって、応答内容の挿入方法を細かくコントロールすることが可能になり、さまざまなユーザー体験を実現することができます。

innerHTML : ターゲット要素の内部HTML を、サーバーからの応答で完全に置き換えます。
outerHTML : ターゲット要素自体を、サーバーからの応答で置き換えます。
beforebegin : ターゲット要素の直前に、サーバーからの応答を挿入します。
afterbegin : ターゲット要素の最初の子として、サーバーからの応答を挿入します。
beforeend : ターゲット要素の最後の子として、サーバーからの応答を挿入します。
afterend : ターゲット要素の直後に、サーバーからの応答を挿入します。
delete : ターゲット要素自体を削除します。
none : サーバーからの応答を挿入せずに無視します。これは、サーバー側で何らかの処理をトリガーするだけで、クライアント側の DOM を更新しない場合に有用です。

hx-swap の記述が無い場合には、innerHTML が指定された事になります。

ケーススタディと実践例

スマートフォン向け新着情報ページの UX向上を考えてみます。

背景

従来のページング処理では、ユーザーが新着情報やお知らせのリストの下部にあるページャーをクリックすると、ページ全体がリロードされ、ブラウザがページの一番上に移動してしまいます。これは、特にスマートフォンユーザーにとって、読みたい情報の続きを見る際に不便を感じさせる体験です。

問題点

  • ページ全体のリロードによる読み込み時間の増加
  • ページ上部への強制移動によるユーザーの不便
  • ユーザーが情報を続けて読む流れが中断される

htmx を用いた改善策

htmx を使用して、ページャーのクリックイベントに対応し、必要な新着情報やお知らせのデータのみを非同期で読み込み、現在のページに動的に追加表示します。これにより、ページのリロードを回避し、ユーザーの現在のスクロール位置を維持できます。

実装手順

  1. htmx ライブラリの導入 : htmxライブラリをプロジェクトに追加し、使用準備を整えます。
  2. ページャーのHTMX化 : ページャーのリンクに hx-get属性を使用し、クリック時に非同期で次のページのコンテンツを取得するよう設定します。
  3. コンテンツの動的追加 : 取得したコンテンツを現在のリストの末尾に hx-targethx-swap を使用して追加します。
  4. スクロール位置の維持 : 新しいコンテンツの読み込み後もユーザーのスクロール位置を維持し、スムーズな閲覧体験を提供します。

成果

  • ユーザーが現在のスクロール位置を維持しながら新しいコンテンツを読むことが可能に
  • よりスムーズで自然な情報閲覧体験の提供
  • ページ全体のリロードを回避し、読み込み時間を短縮

htmx を用いることで、スマートフォン向けウェブサイトの新着情報やお知らせの閲覧体験を大幅に向上させることが可能です。この技術は、ユーザーがより快適に情報を得られるようにするだけでなく、開発者にとっても実装の効率化とメンテナンスの容易さをもたらします。

コミュニティとサポート

残念ながら今の段階(2024年2月)では、日本国内でのコミュニティや日本語でのサポートに関する情報は見つけられていない。 公式の Discord の AROUND THE WORLD に スペイン語・フランス語・トルコ語 というチャンネルがあるので、日本語 のものが追加されて欲しいところです。

2024年2月29日 公式の Discord に #日本語 チャンネルが追加されました。(お願いしてみるもんですね)

公式(英語)

日本語の情報源

最後に

今回は、htmx 入門的な内容を取り扱っていますが、社内では受託制作のプロジェクトにおいても htmx の活用可能性について調査・検証を進めています。htmx は、リクエストされた条件に基づいて必要な部分の HTML のみを返すことで、簡単に利用できるJavaScriptライブラリです。

弊社は、そのバックエンド側で動作するのに適している「a-blog cms」という CMS の開発を手掛けています。実際の活用事例として、a-blog cms の「開発ブログ」等で htmx を用いたテーマの開発についても紹介できればと考えています。

引き続き、htmx 関連の情報についても発信していきたいと思います。




シェアする

Webにまつわる お困りごとをご相談ください。

こんなお手伝いができます

Webコンサルタントとしてのお手伝い/UIデザインのご相談/デジタルメディアの総合プロデュース/パンフレット・DMなどのDTP、ロゴ制作などのビジュアルデザイン