ドキュメント

#htmxの概要

htmxは、JavaScriptではなく、HTMLから直接最新のブラウザ機能にアクセスできるようにするライブラリです。

htmxを理解するために、まずアンカータグを見てみましょう。

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

このアンカータグはブラウザに次のように指示します。

「ユーザーがこのリンクをクリックすると、「/blog」へのHTTP GETリクエストを発行し、レスポンスコンテンツをブラウザウィンドウに読み込みます」。

これを念頭に置いて、次のHTMLを見てみましょう。

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML"
>
    Click Me!
</button>

これはhtmxに次のように指示します。

「ユーザーがこのボタンをクリックすると、「/clicked」へのHTTP POSTリクエストを発行し、レスポンスのコンテンツを使用して、DOM内のIDが`parent-div`の要素を置き換えます」。

htmxは、ハイパーテキストとしてのHTMLの中核となるアイデアを拡張および一般化し、言語内で直接より多くの可能性を開きます。

  • アンカーやフォームだけでなく、あらゆる要素がHTTPリクエストを発行できるようになりました。
  • クリックやフォームの送信だけでなく、あらゆるイベントがリクエストをトリガーできるようになりました。
  • GETPOSTだけでなく、あらゆるHTTP動詞が使用できるようになりました。
  • ウィンドウ全体だけでなく、あらゆる要素がリクエストによる更新のターゲットになることができます。

htmxを使用する場合、サーバー側では通常、_JSON_ではなく_HTML_で応答することに注意してください。これにより、元のWebプログラミングモデルの範囲内に留まり、アプリケーション状態のエンジンとしてのハイパーテキストを使用することができます。その概念を実際に理解する必要さえありません。

必要に応じて、htmxを使用する際にdata-プレフィックスを使用できることは注目に値します。

<a data-hx-post="/click">Click Me!</a>

最後に、バージョン1のhtmxは引き続きサポートされており、IE11をサポートしています。

#1.xから2.xへの移行ガイド

htmx 1.xからhtmx 2.xに移行する場合は、htmx 1.x移行ガイドを参照してください。

intercooler.jsからhtmxに移行する場合は、intercooler移行ガイドを参照してください。

#インストール

Htmxは、依存関係のない、ブラウザ指向のJavaScriptライブラリです。つまり、使用するには、ドキュメントのheadに<script>タグを追加するだけで済みます。ビルドシステムは必要ありません。

#CDN経由(例:unpkg.com)

htmxを始める最も速い方法は、CDN経由でロードすることです。headタグにこれを追加するだけで、開始できます。

<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>

縮小されていないバージョンも利用可能です。

<script src="https://unpkg.com/htmx.org@2.0.3/dist/htmx.js" integrity="sha384-BBDmZzVt6vjz5YbQqZPtFZW82o8QotoM7RUp5xOxV3nSJ8u2pSdtzFAbGKzTlKtg" crossorigin="anonymous"></script>

CDNアプローチは非常にシンプルですが、本番環境ではCDNを使用しないことを検討することをお勧めします。

#コピーをダウンロードする

htmxをインストールする次の最も簡単な方法は、単にプロジェクトにコピーすることです。

htmx.min.jsunpkg.comからダウンロードし、プロジェクトの適切なディレクトリに追加して、必要に応じて<script>タグでインクルードします。

<script src="/path/to/htmx.min.js"></script>

#npm

npmスタイルのビルドシステムの場合、npm経由でhtmxをインストールできます。

npm install htmx.org@2.0.3

インストール後、node_modules/htmx.org/dist/htmx.js(または.min.js)を使用するには、適切なツールを使用する必要があります。たとえば、htmxをいくつかの拡張機能とプロジェクト固有のコードとバンドルすることができます。

#Webpack

webpackを使用してJavaScriptを管理している場合

  • お好みのパッケージマネージャー(npmやyarnなど)を使用してhtmxをインストールします。
  • index.jsにインポートを追加します。
import 'htmx.org';

グローバルなhtmx変数(推奨)を使用する場合は、ウィンドウスコープに挿入する必要があります。

  • カスタムJSファイルを作成します。
  • このファイルをindex.jsにインポートします(手順2のインポートの下)。
import 'path/to/my_custom.js';
  • 次に、このコードをファイルに追加します。
window.htmx = require('htmx.org');
  • 最後に、バンドルを再構築します。

#AJAX

htmxの中核は、HTMLから直接AJAXリクエストを発行できる一連の属性です。

属性説明
hx-get指定されたURLにGETリクエストを発行します。
hx-post指定されたURLにPOSTリクエストを発行します。
hx-put指定されたURLにPUTリクエストを発行します。
hx-patch指定されたURLにPATCHリクエストを発行します。
hx-delete指定されたURLにDELETEリクエストを発行します。

これらの各属性は、AJAXリクエストを発行するURLを取ります。要素がトリガーされると、要素は指定されたタイプの要求を指定されたURLに発行します。

<button hx-put="/messages">
    Put To Messages
</button>

これはブラウザに次のように指示します。

ユーザーがこのボタンをクリックすると、URL /messagesにPUTリクエストを発行し、レスポンスをボタンにロードします。

#リクエストのトリガー

デフォルトでは、AJAXリクエストは要素の「自然な」イベントによってトリガーされます。

  • inputtextarea、およびselectchangeイベントでトリガーされます。
  • formsubmitイベントでトリガーされます。
  • その他すべてはclickイベントによってトリガーされます。

異なる動作が必要な場合は、hx-trigger属性を使用して、リクエストの原因となるイベントを指定できます。

マウスが入力されたときに/mouse_enteredに投稿するdivの例を次に示します。

<div hx-post="/mouse_entered" hx-trigger="mouseenter">
    [Here Mouse, Mouse!]
</div>

#トリガー修飾子

トリガーには、その動作を変更する追加の修飾子をいくつか付けることもできます。たとえば、リクエストを1回だけ実行する場合は、トリガーにonce修飾子を使用できます。

<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
    [Here Mouse, Mouse!]
</div>

トリガーに使用できる他の修飾子は次のとおりです。

  • changed - 要素の値が変更された場合にのみリクエストを発行します。
  • delay:<時間間隔> - リクエストを発行する前に、指定された時間(例:1s)待機します。イベントが再度トリガーされると、カウントダウンがリセットされます。
  • throttle:<時間間隔> - リクエストを発行する前に、指定された時間(例:1s)待機します。delayとは異なり、制限時間が経過する前に新しいイベントが発生した場合、イベントは破棄されるため、リクエストは期間の終わりにトリガーされます。
  • from:<CSSセレクター> - 別の要素のイベントをリッスンします。これは、キーボードショートカットなどに使用できます。このCSSセレクターは、ページが変更されても再評価されないことに注意してください。

これらの属性を使用して、アクティブ検索など、多くの一般的なUXパターンを実装できます。

<input type="text" name="q"
    hx-get="/trigger_delay"
    hx-trigger="keyup changed delay:500ms"
    hx-target="#search-results"
    placeholder="Search..."
>
<div id="search-results"></div>

この入力は、入力が変更された場合、キーアップイベントの500ミリ秒後にリクエストを発行し、結果をIDがsearch-resultsdivに挿入します。

複数のトリガーは、hx-trigger属性でコンマ区切りで指定できます。

#トリガーフィルター

イベント名の後に角かっこを使用してトリガーフィルターを適用し、評価されるJavaScript式を囲むこともできます。式がtrueと評価された場合、イベントはトリガーされます。そうでない場合は、トリガーされません。

要素のControl-Clickでのみトリガーされる例を次に示します。

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

ctrlKeyなどのプロパティは、最初にトリガーイベントに対して解決され、次にグローバルスコープに対して解決されます。this記号は現在の要素に設定されます。

#特別なイベント

htmxは、hx-triggerで使用するための特別なイベントをいくつか提供しています。

  • load - 要素が最初にロードされたときに1回発生します。
  • revealed - 要素が最初にビューポートにスクロールされたときに1回発生します。
  • intersect - 要素が最初にビューポートと交差したときに1回発生します。これは、2つの追加オプションをサポートしています。
    • root:<セレクター> - 交差のルート要素のCSSセレクター。
    • threshold:<浮動小数点数> - 0.0から1.0の間の浮動小数点数。イベントを発生させる交差の量を示します。

高度なユースケースがある場合は、カスタムイベントを使用してリクエストをトリガーすることもできます。

#ポーリング

要素がイベントを待機するのではなく、指定されたURLをポーリングするようにするには、hx-trigger属性でevery構文を使用できます。

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

これはhtmxに次のように指示します。

2秒ごとに、/newsにGETを発行し、レスポンスをdivにロードします。

サーバーの応答からポーリングを停止する場合は、HTTP応答コード286で応答すると、要素はポーリングをキャンセルします。

#ロードポーリング

htmxでポーリングを実現するために使用できるもう1つの手法は、「ロードポーリング」です。この手法では、要素は遅延とともにloadトリガーを指定し、自身をレスポンスに置き換えます。

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

もし/messagesエンドポイントがこの方法で設定されたdivを返し続けると、1秒ごとにURLに「ポーリング」し続けます。

ロードポーリングは、ユーザーにプログレスバーを表示している場合など、ポーリングが終了するエンドポイントを持つ場合に役立ちます。

#リクエストインジケーター

AJAXリクエストが発行された場合、ブラウザはフィードバックを提供しないため、何かが起こっていることをユーザーに知らせることがしばしば重要です。 htmxでは、htmx-indicatorクラスを使用してこれを実現できます。

htmx-indicatorクラスは、このクラスを持つ要素の不透明度がデフォルトで0になるように定義されており、DOMには存在しますが非表示になります。

htmxがリクエストを発行すると、要素(リクエスト元の要素、または指定されている場合は別の要素)にhtmx-requestクラスが配置されます。 htmx-requestクラスは、htmx-indicatorクラスを持つ子要素を不透明度1に遷移させ、インジケーターを表示します。

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

ここにボタンがあります。クリックすると、htmx-requestクラスが追加され、スピナーgif要素が表示されます。(最近はSVGスピナーが好きです。)

htmx-indicatorクラスは不透明度を使用してプログレスインジケーターを非表示および表示しますが、別のメカニズムを使用する場合は、次のように独自の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"/>
</div>

ここでは、インジケーターをIDで明示的に呼び出しています。クラスを親のdivに配置しても同じ効果が得られることに注意してください。

hx-disabled-elt属性を使用することで、リクエスト中は要素にdisabled属性を追加することもできます。

#ターゲット

リクエストを行った要素とは異なる要素にレスポンスをロードする場合は、CSSセレクターを受け取るhx-target属性を使用できます。ライブ検索の例を振り返ってみましょう。

<input type="text" name="q"
    hx-get="/trigger_delay"
    hx-trigger="keyup delay:500ms changed"
    hx-target="#search-results"
    placeholder="Search..."
>
<div id="search-results"></div>

検索結果は、入力タグではなくdiv#search-resultsにロードされることがわかります。

#拡張CSSセレクター

hx-target、およびCSSセレクターを受け取るほとんどの属性は、「拡張」CSS構文をサポートしています。

  • hx-target属性が設定されている要素がターゲットであることを示すthisキーワードを使用できます。
  • closest <CSS selector>構文は、指定されたCSSセレクターに一致する最も近い祖先要素またはそれ自体を見つけます。(例:closest trは、要素に最も近いテーブル行をターゲットにします)
  • next <CSS selector>構文は、指定されたCSSセレクターに一致するDOM内の次の要素を見つけます。
  • previous <CSS selector>構文は、指定されたCSSセレクターに一致するDOM内の前の要素を見つけます。
  • find <CSS selector>は、指定されたCSSセレクターに一致する最初の子孫要素を見つけます。(例:find trは、要素の最初の子孫行をターゲットにします)

さらに、CSSセレクターは、hyperscriptのクエリリテラル構文を模倣して、</>文字で囲むことができます。

このような相対ターゲットは、DOMに多くのid属性を散りばめることなく、柔軟なユーザーインターフェイスを作成するのに役立ちます。

#スワッピング

htmxは、返されたHTMLをDOMにスワップするいくつかの異なる方法を提供します。デフォルトでは、コンテンツはターゲット要素のinnerHTMLを置き換えます。 hx-swap属性を次のいずれかの値で使用することで、これを変更できます。

名前説明
innerHTMLデフォルト。コンテンツをターゲット要素内に配置します。
outerHTMLターゲット要素全体を返されたコンテンツに置き換えます。
afterbeginターゲット内の最初の子の前にコンテンツを挿入します。
beforebeginターゲットの親要素内のターゲットの前にコンテンツを挿入します。
beforeendターゲット内の最後の子の後にコンテンツを追加します。
afterendターゲットの親要素内のターゲットの後にコンテンツを追加します。
deleteレスポンスに関係なく、ターゲット要素を削除します。
noneレスポンスからコンテンツを追加しません(帯域外スワップレスポンスヘッダーは引き続き処理されます)

#モーフィングスワップ

上記の標準スワップメカニズムに加えて、htmxは拡張機能を介して*モーフィング*スワップもサポートしています。モーフィングスワップは、新しいコンテンツを単に置き換えるのではなく、既存のDOMに*マージ*しようとします。より多くのCPUを犠牲にして、スワップ操作中に既存のノードをその場で変更することにより、フォーカス、ビデオの状態などを維持するのに優れていることがよくあります。

モーフィングスタイルのスワップには、次の拡張機能が利用可能です。

  • Idiomorph - htmx開発者によって作成されたモーフィングアルゴリズム。
  • Morphdom Swap - 元のDOMモーフィングライブラリであるmorphdomに基づいています。
  • Alpine-morph - alpine morphプラグインに基づいており、alpine.jsとうまく連携します。

#ビュートランジション

新しい実験的なビュートランジションAPIは、開発者に異なるDOM状態間のアニメーショントランジションを作成する方法を提供します。まだ活発に開発中で、すべてのブラウザで利用できるわけではありませんが、htmxは、特定のブラウザでAPIが利用できない場合に非トランジションメカニズムにフォールバックするこの新しいAPIを操作する方法を提供します。

次の方法を使用して、この新しいAPIを試すことができます。

  • すべてのスワップにトランジションを使用するには、htmx.config.globalViewTransitions設定変数をtrueに設定します。
  • hx-swap属性でtransition:trueオプションを使用します。
  • 上記のいずれかの構成により要素のスワップが遷移される場合は、htmx:beforeTransitionイベントをキャッチし、そのイベントでpreventDefault()を呼び出してトランジションをキャンセルできます。

ビュートランジションは、この機能のChromeドキュメントに概説されているように、CSSを使用して構成できます。

アニメーションの例ページでビュートランジションの例を見ることができます。

#スワップオプション

hx-swap属性は、htmxのスワップ動作を調整するための多くのオプションをサポートしています。たとえば、デフォルトでは、htmxは新しいコンテンツのどこかに見つかったtitleタグのタイトルをスワップします。 ignoreTitle修飾子をtrueに設定することで、この動作をオフにすることができます。

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

hx-swapで使用可能な修飾子は次のとおりです。

オプション説明
transitiontrueまたはfalse。このスワップにビュートランジションAPIを使用するかどうか。
swap古いコンテンツがクリアされてから新しいコンテンツが挿入されるまでのスワップ遅延(例:100ms)。
settle新しいコンテンツが挿入されてから確定するまでの確定遅延(例:100ms)。
ignoreTitletrueに設定されている場合、新しいコンテンツで見つかったタイトルは無視され、ドキュメントのタイトルは更新されません。
scrolltopまたはbottom。ターゲット要素をその上部または下部にスクロールします。
showtopまたはbottom。ターゲット要素の上部または下部が表示されるようにスクロールします。

すべてのスワップ修飾子は、スワップスタイルの指定後にコロンで区切って表示されます。

これらのオプションの詳細については、hx-swapドキュメントを参照してください。

#同期

多くの場合、2つの要素間のリクエストを調整する必要があります。たとえば、ある要素からのリクエストが別の要素のリクエストよりも優先されるようにしたり、別の要素のリクエストが完了するまで待機したりする場合があります。

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への2つの並列リクエストがトリガーされます。

入力でhx-sync="closest form:abort"を使用すると、フォームのリクエストが監視され、フォームリクエストが存在する場合、または入力リクエストの処理中に開始された場合に入力のリクエストが中止されます。

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

これにより、2つの要素間の同期が宣言的な方法で解決されます。

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属性ページにあります。

#CSSトランジション

htmxを使用すると、javascriptなしでCSSトランジションを簡単に使用できます。このHTMLコンテンツを考えてみましょう。

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

このコンテンツがhtmxによってajaxリクエストを介してこの新しいコンテンツに置き換えられるとします。

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

2つのことに注意してください。

  • divは、元のコンテンツと新しいコンテンツで*同じ* IDを持っています。
  • redクラスが新しいコンテンツに追加されました。

この状況を考えると、古い状態から新しい状態へのCSSトランジションを作成できます。

.red {
    color: red;
    transition: all ease-in 1s ;
}

htmxが新しいコンテンツをスワップインする際、CSSトランジションが新しいコンテンツに適用されるように行われ、新しい状態へのスムーズな遷移が実現します。

つまり、要素にCSSトランジションを使用するために必要なのは、リクエスト間でその要素のidを安定させることだけです!

詳細と実際のデモについては、アニメーションの例をご覧ください。

#詳細

htmxにおけるCSSトランジションの実際の動作を理解するには、htmxが使用する基礎となるスワップとセトルのモデルを理解する必要があります。

サーバーから新しいコンテンツを受信すると、コンテンツがスワップインされる前に、ページの既存コンテンツがid属性で一致する要素について検査されます。新しいコンテンツに一致する要素が見つかった場合、スワップが発生する前に、古いコンテンツの属性が新しい要素にコピーされます。その後、新しいコンテンツがスワップインされますが、*古い*属性値が使用されます。最後に、"セトル"遅延(デフォルトでは20ms)の後、新しい属性値がスワップインされます。少し複雑ですが、開発者がJavaScriptを使用することなくCSSトランジションを動作させることができるのは、この仕組みによります。

#アウトオブバンドスワップ

id属性を使用してレスポンスからコンテンツをDOMに直接スワップインしたい場合は、*レスポンス* HTMLでhx-swap-oob属性を使用できます。

<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content

このレスポンスでは、div#messageは一致するDOM要素に直接スワップインされ、追加のコンテンツは通常の方法でターゲットにスワップインされます。

この手法を使用して、他のリクエストに更新を「便乗」させることができます。

#問題のあるテーブル

テーブル要素は、アウトオブバンドスワップと組み合わせると問題が発生する可能性があります。HTMLの仕様では、多くの要素がDOM内で単独では存在できないためです(例:<tr>または<td>)。

この問題を回避するには、templateタグを使用してこれらの要素をカプセル化できます。

<template>
  <tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>

#スワップするコンテンツの選択

レスポンスHTMLのサブセットを選択してターゲットにスワップインしたい場合は、hx-select属性を使用できます。この属性はCSSセレクターを受け取り、レスポンスから一致する要素を選択します。

また、hx-select-oob属性を使用して、アウトオブバンドスワップ用のコンテンツの一部を選択することもできます。この属性は、選択してスワップする要素IDのリストを受け取ります。

#スワップ中のコンテンツの保持

スワップ間で保持したいコンテンツがある場合(例:スワップが発生しても再生を続けたいビデオプレーヤー)、保持したい要素にhx-preserve属性を使用できます。

#パラメータ

デフォルトでは、リクエストの原因となる要素は、値がある場合、その値を含めます。要素がフォームの場合、その中に含まれるすべての入力の値を含めます。

HTMLフォームと同様に、入力のname属性は、htmxが送信するリクエストのパラメータ名として使用されます。

さらに、要素がGET以外のリクエストを引き起こす場合、最も近い外側のフォームのすべての入力の値が含まれます。

他の要素の値を含めたい場合は、hx-include属性と、リクエストに含めたいすべての要素のCSSセレクターを使用できます。

一部のパラメータを除外したい場合は、hx-params属性を使用できます。

最後に、パラメータをプログラムで変更したい場合は、htmx:configRequestイベントを使用できます。

#ファイルのアップロード

htmxリクエストを介してファイルをアップロードしたい場合は、hx-encoding属性をmultipart/form-dataに設定できます。これにより、FormDataオブジェクトを使用してリクエストが送信され、リクエストにファイルが適切に含まれます。

サーバー側のテクノロジーによっては、このタイプの本文コンテンツを含むリクエストの処理方法が大きく異なる場合があることに注意してください。

htmxは、アップロード中に標準のprogressイベントに基づいて、htmx:xhr:progressイベントを定期的に発生させることに注意してください。このイベントにフックして、アップロードの進捗状況を表示できます。

サンプルセクションで、プログレスバーエラー処理など、より高度なフォームパターンを確認してください。

#追加の値

hx-vals属性(JSON形式の名前と式のペア)とhx-vars属性(動的に計算されるコンマ区切りの名前と式のペア)を使用して、リクエストに追加の値を含めることができます。

#リクエストの確認

多くの場合、リクエストを発行する前にアクションを確認したいことがあります。 htmxはhx-confirm属性をサポートしており、シンプルなJavaScriptダイアログを使用してアクションを確認できます。

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

イベントを使用すると、より高度な確認ダイアログを実装できます。 確認の例では、htmxアクションの確認にsweetalert2ライブラリを使用する方法を示しています。

#イベントを使用したリクエストの確認

確認を行うためのもう1つのオプションは、htmx:confirmイベントを使用することです。 このイベントは、リクエストの*すべて*のトリガーで発生し(hx-confirm属性を持つ要素だけではありません)、リクエストの非同期確認を実装するために使用できます。

confirm-with-sweet-alert='true'属性を持つ要素でsweet alertを使用する例を次に示します。

document.body.addEventListener('htmx:confirm', function(evt) {
  if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
    evt.preventDefault();
    swal({
      title: "Are you sure?",
      text: "Are you sure you are sure?",
      icon: "warning",
      buttons: true,
      dangerMode: true,
    }).then((confirmed) => {
      if (confirmed) {
        evt.detail.issueRequest();
      }
    });
  }
});

#属性の継承

htmxのほとんどの属性は継承されます。つまり、属性が設定されている要素とその子要素に適用されます。 これにより、コードの重複を避けるために属性をDOMの上位に「移動」させることができます。 次のhtmxを考えてみましょう。

<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属性が重複しています。 この属性を親要素に移動できます。

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

このhx-confirm属性は、その中にあるすべてのhtmx対応要素に適用されるようになります。

この継承を元に戻したい場合があります。 このグループにキャンセルボタンがあり、確認したくないとします。 次のように、unsetディレクティブを追加できます。

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

上位2つのボタンには確認ダイアログが表示されますが、下部のキャンセルボタンには表示されません。

hx-disinherit属性を使用して、要素ごと、属性ごとに継承を無効にすることができます。

属性の継承を完全に無効にする場合は、htmx.config.disableInheritance設定変数をtrueに設定できます。 これにより、デフォルトで継承が無効になり、hx-inherit属性を使用して明示的に継承を指定できるようになります。

#ブースティング

Htmxは、hx-boost属性を使用して、通常のHTMLアンカーとフォームの「ブースティング」をサポートしています。 この属性は、すべてのアンカータグとフォームを、デフォルトでページの本文をターゲットとするAJAXリクエストに変換します。

例を次に示します。

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

このdiv内のアンカータグは、/blogへのAJAX GETリクエストを発行し、レスポンスをbodyタグにスワップします。

#プログレッシブエンハンスメント

hx-boostの機能の1つは、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-post="/search"
        hx-trigger="keyup changed delay:500ms, search"
        hx-target="#search-results"
        hx-indicator=".htmx-indicator">
</form>

これを配置することで、JavaScriptが有効になっているクライアントは、優れたアクティブ検索UXを引き続き利用できますが、JavaScriptが無効になっているクライアントは、Enterキーを押して検索を実行できます。 さらに、「検索」ボタンを追加することもできます。 その後、action属性をミラーリングしたhx-postをフォームに更新するか、hx-boostを使用する必要があります。

クライアントにレンダリングする内容を正確に判断するには、サーバー側でHX-Requestヘッダーをチェックして、htmx駆動のリクエストと通常のリクエストを区別する必要があります。

他のパターンも同様に適応して、アプリケーションのプログレッシブエンハンスメントのニーズを満たすことができます。

ご覧のとおり、これにはより多くの考慮と作業が必要です。 また、一部の機能は完全に範囲外になります。 これらのトレードオフは、プロジェクトの目標と対象ユーザーに関して、開発者であるあなたが決定する必要があります。

アクセシビリティは、プログレッシブエンハンスメントと密接に関連する概念です。 hx-boostなどのプログレッシブエンハンスメント手法を使用すると、htmxアプリケーションは幅広いユーザーがアクセスできるようになります。

htmxベースのアプリケーションは、htmxがHTML指向であるため、通常の非AJAX駆動のWebアプリケーションと非常によく似ています。

そのため、通常のHTMLアクセシビリティの推奨事項が適用されます。 例えば

  • 可能な限りセマンティックHTMLを使用する(つまり、正しいものに正しいタグを使用する)
  • フォーカス状態が明確に見えるようにする
  • すべてのフォームフィールドにテキストラベルを関連付ける
  • 適切なフォント、コントラストなどを 使用してアプリケーションの可読性を最大限に高める

#WebSocketとSSE

WebSocketとサーバー送信イベント(SSE)は、拡張機能によってサポートされています。 詳細については、SSE拡張機能ページとWebSocket拡張機能ページをご覧ください。

#履歴のサポート

Htmxは、ブラウザ履歴APIと対話するための簡単なメカニズムを提供します。

特定の要素にリクエストURLをブラウザのナビゲーションバーにプッシュし、ページの現在の状態をブラウザの履歴に追加する場合は、hx-push-url属性を含めます。

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

ユーザーがこのリンクをクリックすると、htmxは現在のDOMのスナップショットを取得して保存してから、/blogへのリクエストを行います。 その後、スワップを実行し、新しい場所を履歴スタックにプッシュします。

ユーザーが戻るボタンを押すと、htmxはストレージから古いコンテンツを取得してターゲットにスワップバックし、「戻る」をシミュレートします。 前の状態にします。 キャッシュに場所が見つからない場合、htmxはヘッダーHX-History-Restore-Requestをtrueに設定して、指定されたURLへのajaxリクエストを行い、ページ全体に必要なHTMLを返します。 あるいは、htmx.config.refreshOnHistoryMiss設定変数がtrueに設定されている場合、ブラウザのハードリフレッシュが発行されます。

注意:履歴にURLをプッシュする場合、そのURLに移動して完全なページを取得できる必要があります!ユーザーはURLをコピーしてメールや新しいタブに貼り付けることができます。さらに、ページが履歴キャッシュにない場合、htmxは履歴を復元するときにページ全体を必要とします。

#履歴スナップショット要素の指定

デフォルトでは、htmxはbodyを使用して履歴スナップショットを取得および復元します。これは通常正しい動作ですが、スナップショットに使用する要素を狭めたい場合は、hx-history-elt属性を使用して別の要素を指定できます。

注意: この要素はすべてのページに存在する必要があります。そうでない場合、履歴からの復元は正常に機能しません。

#サードパーティライブラリによるDOM変更の取り消し

サードパーティライブラリを使用していて、htmxの履歴機能を使用したい場合は、スナップショットが作成される前にDOMをクリーンアップする必要があります。select要素をよりリッチなユーザーエクスペリエンスにするTom Selectライブラリを例に考えてみましょう。.tomselectクラスを持つすべてのinput要素をリッチなselect要素に変換するようにTomSelectを設定してみましょう。

まず、新しいコンテンツにクラスを持つ要素を初期化する必要があります

htmx.onLoad(function (target) {
    // find all elements in the new content that should be
    // an editor and init w/ TomSelect
    var editors = target.querySelectorAll(".tomselect")
            .forEach(elt => new TomSelect(elt))
});

これは、.tomselectクラスを持つすべてのinput要素にリッチセレクターを作成します。ただし、これはDOMを変更します。履歴コンテンツが画面にロードされたときにTomSelectが再初期化されるため、この変更を履歴キャッシュに保存したくありません。

これを処理するには、htmx:beforeHistorySaveイベントをキャッチし、それらに対してdestroy()を呼び出すことによってTomSelectの変更をクリーンアップする必要があります

htmx.on('htmx:beforeHistorySave', function() {
    // find all TomSelect elements
    document.querySelectorAll('.tomSelect')
            .forEach(elt => elt.tomselect.destroy()) // and call destroy() on them
})

これにより、DOMが元のHTMLに戻り、クリーンなスナップショットが可能になります。

#履歴スナップショットの無効化

現在のドキュメント内の任意の要素、またはhtmxによって現在のドキュメントにロードされたHTMLフラグメントでhx-history属性をfalseに設定することにより、URLの履歴スナップショットを無効にすることができます。これは、機密データがlocalStorageキャッシュに保存されるのを防ぐために使用できます。これは、共有使用/公共のコンピューターにとって重要です。履歴ナビゲーションは期待どおりに機能しますが、復元時には、ローカル履歴キャッシュではなくサーバーからURLがリクエストされます。

#リクエストとレスポンス

Htmxは、作成するAJAXリクエストへのレスポンスがHTMLであることを期待しています。通常はHTMLフラグメントです(ただし、hx-selectタグと一致する完全なHTMLドキュメントも役立つ場合があります)。htmxは、返されたHTMLを、指定されたターゲットで、指定されたスワップ戦略を使用してドキュメントにスワップします。

スワップで何もしたくないが、クライアント側のイベントをトリガーしたい場合があります(以下を参照)。

この状況では、デフォルトで204 - No Contentレスポンスコードを返すことができ、htmxはレスポンスの内容を無視します。

サーバーからのエラーレスポンス(404または501など)が発生した場合、htmxはhtmx:responseErrorイベントをトリガーします。これは処理できます。

接続エラーが発生した場合、htmx:sendErrorイベントがトリガーされます。

#レスポンス処理の設定

htmx.config.responseHandling配列を変更または置換することで、htmxの上記の動作を設定できます。このオブジェクトは、次のように定義されたJavaScriptオブジェクトのコレクションです。

    responseHandling: [
        {code:"204", swap: false},   // 204 - No Content by default does nothing, but is not an error
        {code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped
        {code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors
        {code:"...", swap: false}    // catch all for any other response code
    ]

htmxがレスポンスを受信すると、htmx.config.responseHandling配列を順番に反復処理し、指定されたオブジェクトのcodeプロパティが正規表現として扱われたときに現在のレスポンスと一致するかどうかをテストします。エントリが現在のレスポンスコードと一致する場合、レスポンスを処理するかどうか、およびどのように処理するかを決定するために使用されます。

この配列のエントリでレスポンス処理の設定に使用できるフィールドは次のとおりです。

  • code - レスポンスコードに対してテストされる正規表現を表す文字列。
  • swap - レスポンスをDOMにスワップする場合はtrue、そうでない場合はfalse
  • error - htmxがこのレスポンスをエラーとして扱う場合はtrue
  • ignoreTitle - htmxがレスポンスのtitleタグを無視する場合はtrue
  • select - レスポンスからコンテンツを選択するために使用するCSSセレクター
  • target - レスポンスの代替ターゲットを指定するCSSセレクター
  • swapOverride - レスポンスの代替スワップメカニズム

#レスポンス処理の設定例

この設定の使用方法の例として、サーバーサイドフレームワークが検証エラーが発生したときに422 - Unprocessable Entityレスポンスで応答する状況を考えてみましょう。デフォルトでは、htmxは正規表現[45]..と一致するため、レスポンスを無視します。

responseHandlingを設定するためのメタ設定メカニズムを使用して、次の設定を追加できます

<!--
  * 204 No Content by default does nothing, but is not an error
  * 2xx, 3xx and 422 responses are non-errors and are swapped
  * 4xx & 5xx responses are not swapped and are errors
  * all other responses are swapped using "..." as a catch-all
-->
<meta
	name="htmx-config"
	content='{
        "responseHandling":[
            {"code":"204", "swap": false},
            {"code":"[23]..", "swap": true},
            {"code":"422", "swap": true},
            {"code":"[45]..", "swap": false, "error":true},
            {"code":"...", "swap": true}
        ]
    }'
/>

HTTPレスポンスコードに関係なくすべてをスワップする場合は、次の設定を使用できます

<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--all responses are swapped-->

最後に、レスポンスタarget拡張機能を使用することを検討する価値があります。これにより、属性を介してレスポンスコードの動作を宣言的に設定できます。

#CORS

クロスオリジンコンテキストでhtmxを使用する場合は、htmxヘッダーがクライアント側で表示されるように、WebサーバーがAccess-Controlヘッダーを設定するように構成してください。

htmxが実装するすべてのリクエストヘッダーとレスポンスヘッダーを参照してください。

#リクエストヘッダー

htmxはリクエストに多くの便利なヘッダーを含んでいます

ヘッダー説明
HX-Boostedhx-boostを使用している要素を介したリクエストであることを示します
HX-Current-URLブラウザの現在のURL
HX-History-Restore-Requestローカル履歴キャッシュでミスが発生した後の履歴復元のリクエストの場合は「true」
HX-Prompthx-promptに対するユーザーレスポンス
HX-Request常に「true」
HX-Targetターゲット要素のid(存在する場合)
HX-Trigger-Nameトリガーされた要素のname(存在する場合)
HX-Triggerトリガーされた要素のid(存在する場合)

#レスポンスヘッダー

htmxは、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-After-Settle - settleステップ後にクライアント側イベントをトリガーできます
  • HX-Trigger-After-Swap - swapステップ後にクライアント側イベントをトリガーできます

HX-Triggerヘッダーの詳細については、HX-Triggerレスポンスヘッダーを参照してください。

htmxを介してフォームを送信することには、Post/Redirect/Getパターンが不要になるという利点があります。サーバーでPOSTリクエストを正常に処理した後、HTTP 302 (リダイレクト)を返す必要はありません。新しいHTMLフラグメントを直接返すことができます。

また、上記のレスポンスヘッダーは、HTTP 302 (リダイレクト)などの3xxリダイレクトレスポンスコードではhtmxに処理用として提供されません。代わりに、ブラウザは内部でリダイレクトをインターセプトし、リダイレクトされたURLからのヘッダーとレスポンスを返します。可能な場合は、これらのレスポンスヘッダーの返却を許可するために、200などの代替レスポンスコードを使用してください。

#リクエストの操作順序

htmxリクエストの操作順序は次のとおりです

  • 要素がトリガーされ、リクエストが開始されます
    • リクエストの値が収集されます
    • htmx-requestクラスが適切な要素に適用されます
    • リクエストはAJAXを介して非同期に発行されます
      • レスポンスを取得すると、ターゲット要素はhtmx-swappingクラスでマークされます
      • オプションのスワップ遅延が適用されます(hx-swap属性を参照)
      • 実際の内容スワップが行われます
        • htmx-swappingクラスがターゲットから削除されます
        • htmx-addedクラスが新しいコンテンツの各部分に追加されます
        • htmx-settlingクラスがターゲットに適用されます
        • settle遅延が行われます(デフォルト:20ms)
        • DOMが確定されます
        • htmx-settlingクラスがターゲットから削除されます
        • htmx-addedクラスが新しいコンテンツの各部分から削除されます

htmx-swapping クラスと htmx-settling クラスを使用して、ページ間の CSSトランジション を作成できます。

#バリデーション

Htmx は HTML5 Validation API と統合されており、検証可能な入力が無効な場合、フォームのリクエストを発行しません。これは、AJAXリクエストとWebSocket送信の両方で当てはまります。

Htmxは、カスタムバリデーションとエラー処理をフックするために使用できる、バリデーションに関するイベントを発生させます。

  • htmx:validation:validate - 要素の checkValidity() メソッドが呼び出される前に呼び出されます。カスタム検証ロジックを追加するために使用できます。
  • htmx:validation:failed - checkValidity() がfalseを返し、無効な入力を示している場合に呼び出されます。
  • htmx:validation:halted - バリデーションエラーが原因でリクエストが発行されない場合に呼び出されます。具体的なエラーは event.detail.errors オブジェクトにあります。

フォーム以外の要素は、デフォルトではリクエストを行う前に検証されませんが、hx-validate 属性を「true」に設定することで、検証を有効にすることができます。

#バリデーションの例

hx-on 属性を使用して htmx:validation:validate イベントをキャッチし、入力値が foo であることを要求する入力の例を次に示します。

<form id="example-form" hx-post="/test">
    <input name="example"
           onkeyup="this.setCustomValidity('') // reset the validation on keyup"
           hx-on:htmx:validation:validate="if(this.value != 'foo') {
                    this.setCustomValidity('Please enter the value foo') // set the validation error
                    htmx.find('#example-form').reportValidity()          // report the issue
                }">
</form>

クライアント側のバリデーションはすべて、常にバイパスされる可能性があるため、サーバー側で再実行する必要があることに注意してください。

#アニメーション

Htmxでは、HTMLとCSSのみを使用して、多くの状況でCSSトランジションを使用できます。

利用可能なオプションの詳細については、アニメーションガイドを参照してください。

#拡張機能

htmxは、ライブラリの動作をカスタマイズできる拡張機能メカニズムを提供します。拡張機能はJavaScriptで定義され、hx-ext 属性を介して有効になります。

htmxスワップにIdiomorph DOMモーフィングアルゴリズムを使用できるidiomorph 拡張機能をインストールする方法を次に示します。

<head>
  <script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph-ext.min.js"></script>
</head>
<body hx-ext="morph">
  ...
  <button hx-post="/example" hx-swap="morph" hx-target="#content">
    Update Content
  </button>
  ...
</body>

最初に拡張機能が含まれ(htmx.js が含まれた*後*に含まれる必要があります)、次に hx-ext 属性を介して名前で拡張機能が参照されます。これにより、morph スワップを使用できるようになります。

#コア拡張機能

htmxは、htmx開発チームによってサポートされているいくつかの「コア」拡張機能をサポートしています。

  • head-support - htmxリクエストでのheadタグ情報(スタイルなど)のマージをサポートします |
  • htmx-1-compat - htmx 1のデフォルトと機能を復元します
  • idiomorph - idiomorphを使用してmorphスワップ戦略をサポートします
  • preload - パフォーマンス向上のためコンテンツをプリロードできます
  • response-targets - HTTPレスポンスコード(例:404)に基づいて要素をターゲットにすることができます
  • sse - Server Sent Eventsのサポート
  • ws - Web Socketsのサポート

利用可能なすべての拡張機能は、拡張機能ページで確認できます。

#拡張機能の作成

独自の拡張機能をhtmxに追加することに興味がある場合は、拡張機能のドキュメントを参照してください。

#イベントとロギング

Htmxには、ロギングシステムとしても機能する、広範なイベントメカニズムがあります。

特定のhtmxイベントに登録する場合、以下を使用できます。

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

または、必要に応じて、次のhtmxヘルパーを使用できます。

htmx.on("htmx:load", function(evt) {
    myJavascriptLib.init(evt.detail.elt);
});

htmx:load イベントは、htmxによって要素がDOMにロードされるたびに発生し、通常の load イベントと事実上同等です。

htmxイベントの一般的な用途は次のとおりです。

#イベントでサードパーティライブラリを初期化する

htmx:load イベントを使用してコンテンツを初期化することは非常に一般的であるため、htmxはヘルパー関数を備えています。

htmx.onLoad(function(target) {
    myJavascriptLib.init(target);
});

これは最初の例と同じことを行いますが、少しすっきりしています。

#イベントでリクエストを設定する

AJAXリクエストが発行される前に変更するために、htmx:configRequest イベントを処理できます。

document.body.addEventListener('htmx:configRequest', function(evt) {
    evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the request
    evt.detail.headers['Authentication-Token'] = getAuthToken(); // add a new header into the request
});

ここでは、リクエストが送信される前に、パラメーターとヘッダーをリクエストに追加します。

#イベントによるスワップ動作の変更

htmxのスワップ動作を変更するために、htmx:beforeSwap イベントを処理できます。

document.body.addEventListener('htmx:beforeSwap', function(evt) {
    if(evt.detail.xhr.status === 404){
        // alert the user when a 404 occurs (maybe use a nicer mechanism than alert())
        alert("Error: Could Not Find Resource");
    } else if(evt.detail.xhr.status === 422){
        // allow 422 responses to swap as we are using this as a signal that
        // a form was submitted with bad data and want to rerender with the
        // errors
        //
        // set isError to false to avoid error logging in console
        evt.detail.shouldSwap = true;
        evt.detail.isError = false;
    } else if(evt.detail.xhr.status === 418){
        // if the response code 418 (I'm a teapot) is returned, retarget the
        // content of the response to the element with the id `teapot`
        evt.detail.shouldSwap = true;
        evt.detail.target = htmx.find("#teapot");
    }
});

ここでは、通常はhtmxでスワップを行わない、いくつかの400レベルのエラーレスポンスコードを処理します。

#イベントの命名

すべてのイベントは、2つの異なる名前で発生することに注意してください。

  • キャメルケース
  • ケバブケース

そのため、たとえば、htmx:afterSwap または htmx:after-swap をリッスンできます。これは、他のライブラリとの相互運用性を促進します。Alpine.js たとえば、ケバブケースが必要です。

#ロギング

htmx.logger にロガーを設定すると、すべてのイベントがログに記録されます。これは、トラブルシューティングに非常に役立ちます。

htmx.logger = function(elt, event, data) {
    if(console) {
        console.log(event, elt, data);
    }
}

#デバッグ

htmx(または他の宣言型言語)を使用した宣言型およびイベント駆動型プログラミングは、素晴らしく非常に生産的なアクティビティになる可能性がありますが、命令型アプローチと比較した場合の1つの欠点は、デバッグが難しいことです。

たとえば、コツがわからないと、何かが*起こらない*理由を理解するのが難しい場合があります。

さて、ここにコツがあります

使用できる最初のデバッグツールは、htmx.logAll() メソッドです。これは、htmxがトリガーするすべてのイベントをログに記録し、ライブラリが何をしているかを正確に確認できるようにします。

htmx.logAll();

もちろん、それはhtmxが何かを*しない*理由を教えてくれません。また、DOM要素がトリガーとして使用するために*どの*イベントを発生させているかわからない場合があります。これを解決するには、ブラウザコンソールで使用可能なmonitorEvents() メソッドを使用できます。

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

これは、IDが theElement の要素で発生しているすべてのイベントをコンソールに出力し、何が起こっているかを正確に確認できるようにします。

これはコンソールからのみ機能し、ページのスクリプトタグに埋め込むことはできません。

最後に、プッシュが来たら、最小化されていないバージョンをロードすることで、htmx.js をデバッグしたい場合があります。約2500行のJavaScriptなので、乗り越えられない量のコードではありません。何が起こっているかを確認するには、issueAjaxRequest() メソッドと handleAjaxResponse() メソッドにブレークポイントを設定するのが最も likely です。

また、助けが必要な場合は、いつでもDiscord にお気軽にご参加ください。

#デモの作成

バグをデモンストレーションしたり、使用方法を明確にするために、jsfiddle のようなJavaScriptスニペットサイトを使用できると便利な場合があります。簡単なデモ作成を容易にするために、htmxは次のものをインストールするデモスクリプトサイトをホストしています。

  • htmx
  • ハイパースクリプト
  • リクエストモックライブラリ

デモ/フィドル/何でもに次のスクリプトタグを追加するだけです。

<script src="https://demo.htmx.org"></script>

このヘルパーを使用すると、どのURLを示すかを url 属性で template タグを追加することにより、モックレスポンスを追加できます。そのURLへの応答はテンプレートのinnerHTMLになり、モックレスポンスを簡単に構築できます。 delay 属性を使用して応答に遅延を追加できます。これは、遅延するミリ秒数を示す整数である必要があります。

${} 構文を使用して、テンプレートに単純な式を埋め込むことができます。

これはデモにのみ使用されるべきであり、常に最新バージョンのhtmxとハイパースクリプトを取得するため、長期間動作することが保証されていないことに注意してください。

#デモ例

動作中のコードの例を次に示します。

<!-- load demo environment -->
<script src="https://demo.htmx.org"></script>

<!-- post to /foo -->
<button hx-post="/foo" hx-target="#result">
    Count Up
</button>
<output id="result"></output>

<!-- respond to /foo with some dynamic content in a template tag -->
<script>
    globalInt = 0;
</script>
<template url="/foo" delay="500"> <!-- note the url and delay attributes -->
    ${globalInt++}
</template>

#スクリプティング

htmxはWebアプリケーションの構築に対するハイパーメディアアプローチを推奨していますが、クライアントスクリプティングの多くのオプションを提供しています。スクリプティングは、WebアーキテクチャのRESTfulな説明に含まれています。 Code-On-Demand を参照してください。可能な限り、Webアプリケーションでのスクリプト作成には、ハイパーメディアフレンドリーなアプローチをお勧めします。

htmxとスクリプトソリューションの主な統合ポイントは、htmxが送信し、応答できるイベントです。サードパーティJavaScriptセクションのSortableJSの例を参照して、イベントを介してJavaScriptライブラリをhtmxと統合するための適切なテンプレートを確認してください。

htmxとよく組み合わせるスクリプトソリューションには、次のものがあります。

  • VanillaJS - JavaScriptの組み込み機能を使用してイベントハンドラーをフックし、htmxが発行するイベントに応答するだけで、スクリプト作成に非常に効果的です。これは非常に軽量で、ますます人気が高まっているアプローチです。
  • AlpineJS - Alpine.jsは、リアクティブプログラミングサポートを含む、洗練されたフロントエンドスクリプトを作成するための豊富なツールセットを提供しながら、非常に軽量です。Alpineは、htmxとよく組み合わせることができる「インラインスクリプト」アプローチを推奨しています。
  • jQuery - 一部のサークルではその年齢と評判にもかかわらず、jQueryはhtmxと非常によく組み合わせられます。特に、すでに多くのjQueryを持っている古いコードベースではそうです。
  • hyperscript - Hyperscriptは、htmxを作成したのと同じチームによって作成された実験的なフロントエンドスクリプト言語です。HTMLにうまく埋め込まれ、イベントに応答してイベントを作成するように設計されており、htmxと非常によく組み合わせられます。

私たちの本には、「クライアントサイドスクリプティング」というタイトルの章全体があり、htmxベースのアプリケーションにスクリプトを統合する方法について説明しています。

#hx-on* 属性

HTMLでは、onevent プロパティonClick など)を介してインラインスクリプトを埋め込むことができます。

<button onclick="alert('You clicked me!')">
    Click Me!
</button>

この機能により、スクリプティングロジックをロジックが適用されるHTML要素と共存させることができ、優れた動作の局所性(LoB)が得られます。残念ながら、HTMLは特定のDOMイベント(例:onclick)の固定数に対してのみ on* 属性を許可し、要素の任意のイベントに応答するための一般化されたメカニズムを提供していません。

この欠点を解消するために、htmxはhx-on*属性を提供しています。これらの属性を使用すると、標準のon*プロパティの挙動(LoB)を維持したまま、あらゆるイベントに応答することができます。

hx-on属性を使用してclickイベントに応答したい場合は、次のように記述します。

<button hx-on:click="alert('You clicked me!')">
    Click Me!
</button>

つまり、文字列hx-onの後にコロン(またはダッシュ)、そしてイベント名を続けます。

もちろん、clickイベントの場合は、標準のonclick属性を使用することをお勧めします。ただし、htmx:config-requestイベントを使用してリクエストにパラメータを追加したいhtmx対応のボタンを考えてみましょう。これは標準のon*プロパティでは不可能ですが、hx-on:htmx:config-request属性を使用することで実現できます。

<button hx-post="/example"
        hx-on:htmx:config-request="event.detail.parameters.example = 'Hello Scripting!'">
    Post Me!
</button>

ここでは、exampleパラメータが、発行される前に値「Hello Scripting!」とともにPOSTリクエストに追加されています。

hx-on*属性は、汎用的な埋め込みスクリプトのための非常にシンプルなメカニズムです。AlpineJSやhyperscriptのような、より本格的なフロントエンドスクリプトソリューションの代替となるものではありません。しかし、htmx対応アプリケーションでVanillaJSベースのスクリプトアプローチを補完することができます。

HTML属性は大文字と小文字を区別しないことに注意してください。これは残念ながら、大文字/小文字の区別(キャメルケース)に依存するイベントには対応できないことを意味します。キャメルケースのイベントをサポートする必要がある場合は、AlpineJSやhyperscriptなどの、より完全な機能を備えたスクリプトソリューションを使用することをお勧めします。htmxは、まさにこの理由から、すべてのイベントをキャメルケースとケバブケースの両方でディスパッチします。

#サードパーティのJavaScript

Htmxは、サードパーティのライブラリとかなりうまく統合されています。ライブラリが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>

Sortableでは、ほとんどのJavaScriptライブラリと同様に、ある時点でコンテンツを初期化する必要があります。

jQueryでは、次のように行う場合があります。

$(document).ready(function() {
    var sortables = document.body.querySelectorAll(".sortable");
    for (var i = 0; i < sortables.length; i++) {
        var sortable = sortables[i];
        new Sortable(sortable, {
            animation: 150,
            ghostClass: 'blue-background-class'
        });
    }
});

htmxでは、代わりにhtmx.onLoad関数を使用し、ドキュメント全体ではなく、新しく読み込まれたコンテンツからのみ選択します。

htmx.onLoad(function(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'
        });
    }
})

これにより、htmxによって新しいコンテンツがDOMに追加されると、sortable要素が適切に初期化されます。

JavaScriptがhtmx属性を持つコンテンツをDOMに追加する場合、このコンテンツがhtmx.process()関数で初期化されていることを確認する必要があります。

たとえば、fetch APIを使用してデータを取得し、それをdivに配置する場合、そのHTMLにhtmx属性が含まれている場合は、次のようにhtmx.process()の呼び出しを追加する必要があります。

let myDiv = document.getElementById('my-div')
fetch('http://example.com/movies.json')
    .then(response => response.text())
    .then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } );

一部のサードパーティライブラリは、HTMLテンプレート要素からコンテンツを作成します。たとえば、Alpine JSはテンプレートのx-if属性を使用して、条件付きでコンテンツを追加します。このようなテンプレートは、最初はDOMの一部ではなく、htmx属性が含まれている場合は、読み込み後にhtmx.process()を呼び出す必要があります。次の例では、Alpineの$watch関数を使用して、条件付きコンテンツをトリガーする値の変更を探します。

<div x-data="{show_new: false}"
    x-init="$watch('show_new', value => {
        if (show_new) {
            htmx.process(document.querySelector('#new_content'))
        }
    })">
    <button @click = "show_new = !show_new">Toggle New Content</button>
    <template x-if="show_new">
        <div id="new_content">
            <a hx-get="/server/newstuff" href="#">New Clickable</a>
        </div>
    </template>
</div>

#Webコンポーネント

htmxとWebコンポーネントを統合する方法の例については、Webコンポーネントの例ページを参照してください。

#キャッシング

htmxは、標準のHTTPキャッシングメカニズムをそのまま使用できます。

サーバーが特定のURLへのレスポンスにLast-Modified HTTPレスポンスヘッダーを追加する場合、ブラウザは同じURLへの次のリクエストにIf-Modified-SinceリクエストHTTPヘッダーを自動的に追加します。サーバーが他のヘッダーに応じて同じURLに対して異なるコンテンツをレンダリングできる場合、VaryレスポンスHTTPヘッダーを使用する必要があることに注意してください。たとえば、サーバーがHX-Requestヘッダーがない場合、またはfalseの場合に完全なHTMLをレンダリングし、HX-Request: trueの場合にそのHTMLのフラグメントをレンダリングする場合は、Vary: HX-Requestを追加する必要があります。これにより、キャッシュはレスポンスURLのみに基づくのではなく、レスポンスURLとHX-Requestリクエストヘッダーの組み合わせに基づいてキー設定されます。

Varyヘッダーを使用できない(または使用したくない)場合は、代わりに設定パラメータgetCacheBusterParamtrueに設定できます。この設定変数が設定されている場合、htmxは、行うGETリクエストにキャッシュバスターパラメータを含めます。これにより、ブラウザがhtmxベースのレスポンスとhtmxベースでないレスポンスを同じキャッシュスロットにキャッシュすることを防ぎます。

htmxは、ETagとも期待どおりに動作します。サーバーが同じURLに対して異なるコンテンツをレンダリングできる場合(たとえば、HX-Requestヘッダーの値に応じて)、サーバーは各コンテンツに対して異なるETagを生成する必要があることに注意してください。

#セキュリティ

htmxを使用すると、DOMに直接ロジックを定義できます。これには多くの利点があり、最大の利点は挙動の局所性であり、システムの理解と保守が容易になります。

ただし、このアプローチに関する懸念事項はセキュリティです。htmxはHTMLの表現力を高めるため、悪意のあるユーザーがアプリケーションにHTMLを挿入できる場合、htmxのこの表現力を悪用して悪意のある目的を達成する可能性があります。

#ルール1:すべてのユーザーコンテンツをエスケープする

HTMLベースのWeb開発の最初のルールは、常に次のとおりです。ユーザーからの入力を信頼しないでください。サイトに挿入されるすべてのサードパーティの信頼できないコンテンツをエスケープする必要があります。これは、とりわけ、クロスサイトスクリプティング攻撃を防ぐためです。

XSSとその防止方法に関する広範なドキュメントは、優れたOWASP Webサイトにあり、クロスサイトスクリプティング防止チートシートが含まれています。

良いニュースは、これは非常に古く、よく理解されているトピックであり、サーバー側のテンプレート言語の大部分は、まさにそのような問題を防ぐためにコンテンツの自動エスケープをサポートしていることです。

とはいえ、人々がより危険な方法でHTMLを挿入することを選択する場合があり、多くの場合、テンプレート言語の何らかのraw()メカニズムを介して行われます。これは正当な理由で行うことができますが、挿入されるコンテンツがサードパーティからのものである場合、hx-およびdata-hxで始まる属性、インライン<script>タグなどを削除するなど、必ずスクラブする必要があります。

生のHTMLを挿入し、独自のエスケープを行っている場合、許可しない属性とタグをブラックリストに登録するのではなく、許可する属性とタグをホワイトリストに登録するのがベストプラクティスです。

#htmxセキュリティツール

もちろん、バグは発生し、開発者は完璧ではないため、Webアプリケーションのセキュリティには階層型のアプローチを採用するのが適切であり、htmxはアプリケーションのセキュリティを確保するためのツールも提供しています。

それらを見てみましょう。

#hx-disable

htmxがアプリケーションのセキュリティをさらに強化するために提供する最初のツールは、hx-disable属性です。この属性は、特定の要素とその中のすべての要素のすべてのhtmx属性の処理を防ぎます。そのため、たとえば、テンプレートに生のHTMLコンテンツを含めている場合(繰り返しますが、これはお勧めしません!)、コンテンツの周りにhx-disable属性を持つdivを配置できます。

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

htmxは、そのコンテンツで見つかったhtmx関連の属性や機能を処理しません。この属性は、さらにコンテンツを挿入することによって無効にすることはできません。hx-disable属性が要素の親階層のどこかに見つかった場合、htmxによって処理されません。

#hx-history

もう1つのセキュリティ上の考慮事項は、htmx履歴キャッシュです。ユーザーのlocalStorageキャッシュに保存したくない機密データを含むページがある場合があります。ページのどこかにhx-history属性を含め、その値をfalseに設定することにより、特定のページを履歴キャッシュから省略できます。

#構成オプション

htmxは、セキュリティ関連の構成オプションも提供しています。

  • htmx.config.selfRequestsOnly - trueに設定されている場合、現在のドキュメントと同じドメインへのリクエストのみが許可されます。
  • htmx.config.allowScriptTags - htmxは、読み込まれた新しいコンテンツで見つかった<script>タグを処理します。この動作を無効にする場合は、この設定変数をfalseに設定できます。
  • htmx.config.historyCacheSize - localStorageキャッシュにHTMLを保存しないように、0に設定できます。
  • htmx.config.allowEval - falseに設定して、evalに依存するhtmxのすべての機能を無効にすることができます。
    • イベントフィルター
    • hx-on:属性
    • プレフィックスjs:が付いたhx-vals
    • プレフィックスjs:が付いたhx-headers

eval()を無効にすることで削除されたすべての機能は、独自のJavascriptとhtmxイベントモデルを使用して再実装できることに注意してください。

#イベント

現在のホスト以外のドメインへのリクエストを許可したいが、完全にオープンにしたくない場合は、htmx:validateUrlイベントを使用できます。このイベントでは、リクエストURLがdetail.urlスロット、およびsameHostプロパティで使用可能です。

これらの値を検査し、リクエストが有効でない場合は、イベントでpreventDefault()を呼び出して、リクエストが発行されないようにすることができます。

document.body.addEventListener('htmx:validateUrl', function (evt) {
  // only allow requests to the current server as well as myserver.com
  if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
    evt.preventDefault();
  }
});

#CSPオプション

ブラウザは、Webアプリケーションをさらに保護するためのツールも提供しています。最も強力なツールは、Content Security Policy(CSP)です。CSPを使用すると、たとえば、オリジン以外のホストへのリクエストを発行しない、インラインスクリプトタグを評価しないなど、ブラウザに指示できます。

metaタグのCSPの例を以下に示します。

    <meta http-equiv="Content-Security-Policy" content="default-src 'self';">

これはブラウザに「元の(ソース)ドメインへの接続のみを許可する」ように指示します。これはhtmx.config.selfRequestsOnlyと重複しますが、アプリケーションセキュリティを扱う場合、セキュリティへの階層化されたアプローチは正当化され、実際には理想的です。

CSPの完全な説明はこのドキュメントの範囲外ですが、MDNの記事はこのトピックを調べるための良い出発点を提供しています。

#htmxの設定

Htmxには、プログラムまたは宣言的にアクセスできる設定オプションがいくつかあります。それらを以下にリストします。

設定変数情報
htmx.config.historyEnabledデフォルトはtrue、実際にはテストにのみ役立ちます
htmx.config.historyCacheSizeデフォルトは10
htmx.config.refreshOnHistoryMissデフォルトはfalsetrueに設定すると、htmxは履歴ミスでAJAXリクエストを使用するのではなく、フルページリフレッシュを発行します
htmx.config.defaultSwapStyleデフォルトはinnerHTML
htmx.config.defaultSwapDelayデフォルトは0
htmx.config.defaultSettleDelayデフォルトは20
htmx.config.includeIndicatorStylesデフォルトはtrue(インジケータースタイルがロードされるかどうかを決定します)
htmx.config.indicatorClassデフォルトはhtmx-indicator
htmx.config.requestClassデフォルトはhtmx-request
htmx.config.addedClassデフォルトはhtmx-added
htmx.config.settlingClassデフォルトはhtmx-settling
htmx.config.swappingClassデフォルトはhtmx-swapping
htmx.config.allowEvalデフォルトはtrue、特定の機能(例:トリガーフィルター)に対してhtmxのevalの使用を無効にするために使用できます
htmx.config.allowScriptTagsデフォルトはtrue、htmxが新しいコンテンツで見つかったスクリプトタグを処理するかどうかを決定します
htmx.config.inlineScriptNonceデフォルトは''、つまり、インラインスクリプトにnonceが追加されないことを意味します
htmx.config.attributesToSettleデフォルトは["class", "style", "width", "height"]、セトリングフェーズ中にセトルする属性
htmx.config.inlineStyleNonceデフォルトは''、つまり、インラインスタイルにnonceが追加されないことを意味します
htmx.config.useTemplateFragmentsデフォルトはfalse、サーバーからコンテンツを解析するためのHTMLテンプレートタグ(IE11互換性なし!)
htmx.config.wsReconnectDelayデフォルトはfull-jitter
htmx.config.wsBinaryTypeデフォルトはblob、WebSocket接続を介して受信されるバイナリデータのタイプ
htmx.config.disableSelectorデフォルトは[hx-disable], [data-hx-disable]、htmxはこの属性が設定されている要素または親要素を処理しません
htmx.config.withCredentialsデフォルトはfalse、Cookie、認証ヘッダー、TLSクライアント証明書などの資格情報を使用してクロスサイトのアクセス制御リクエストを許可します
htmx.config.timeoutデフォルトは0、リクエストが自動的に終了するまでにリクエストが実行できるミリ秒数
htmx.config.scrollBehaviorデフォルトは「instant」、show修飾子をhx-swapと共に使用する場合のスクロール動作。許可される値は、instant(スクロールは1回のジャンプで瞬時に行われるべき)、smooth(スクロールはスムーズにアニメーション化するべき)、およびauto(スクロール動作はscroll-behaviorの計算値によって決定される)です。
htmx.config.defaultFocusScrollフォーカスされている要素をビューにスクロールする必要があるかどうか、デフォルトはfalseで、focus-scroll swap修飾子を使用してオーバーライドできます。
htmx.config.getCacheBusterParamデフォルトはfalse、trueに設定すると、htmxはターゲット要素をorg.htmx.cache-buster=targetElementIdの形式でGETリクエストに追加します
htmx.config.globalViewTransitionstrueに設定すると、htmxは新しいコンテンツをスワップするときにView Transition APIを使用します。
htmx.config.methodsThatUseUrlParamsデフォルトは["get", "delete"]、htmxはリクエストボディではなくURLにパラメータをエンコードすることにより、これらのメソッドでリクエストをフォーマットします
htmx.config.selfRequestsOnlyデフォルトはtrue、現在のドキュメントと同じドメインへのAJAXリクエストのみを許可するかどうか
htmx.config.ignoreTitleデフォルトはfalsetrueに設定すると、新しいコンテンツでtitleタグが見つかった場合、htmxはドキュメントのタイトルを更新しません
htmx.config.disableInheritancehtmxの属性継承を無効にし、hx-inherit属性でオーバーライドできます
htmx.config.scrollIntoViewOnBoostデフォルトは`true`で、ブーストされた要素のターゲットをビューポートにスクロールするかどうかを示します。ブーストされた要素で`hx-target`が省略されている場合、ターゲットはデフォルトで`body`になり、ページが上部にスクロールします。
htmx.config.triggerSpecsCacheデフォルトは`null`で、評価されたトリガースペックを保存するキャッシュです。メモリ使用量を増やす代わりに解析パフォーマンスを向上させます。単純なオブジェクトを定義して、クリアされないキャッシュを使用するか、プロキシオブジェクトを使用して独自のシステムを実装できます。
htmx.config.responseHandlingレスポンスステータスコードのデフォルトのレスポンス処理動作は、ここでスワップまたはエラーに設定できます。
htmx.config.allowNestedOobSwapsデフォルトは`true`で、メインレスポンス要素内にネストされている要素でOOBスワップを処理するかどうかを示します。ネストされたOOBスワップを参照してください。

JavaScriptで直接設定することも、metaタグを使用することもできます。

<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>

#結論

以上です!

htmxをお楽しみください! たくさんのことを、たくさんのコードを書かずに達成できます!