目次
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の中核となるアイデアを拡張および一般化し、言語内で直接より多くの可能性を開きます。
GET
とPOST
だけでなく、あらゆるHTTP動詞が使用できるようになりました。htmxを使用する場合、サーバー側では通常、_JSON_ではなく_HTML_で応答することに注意してください。これにより、元のWebプログラミングモデルの範囲内に留まり、アプリケーション状態のエンジンとしてのハイパーテキストを使用することができます。その概念を実際に理解する必要さえありません。
必要に応じて、htmxを使用する際にdata-
プレフィックスを使用できることは注目に値します。
<a data-hx-post="/click">Click Me!</a>
最後に、バージョン1のhtmxは引き続きサポートされており、IE11をサポートしています。
htmx 1.xからhtmx 2.xに移行する場合は、htmx 1.x移行ガイドを参照してください。
intercooler.jsからhtmxに移行する場合は、intercooler移行ガイドを参照してください。
Htmxは、依存関係のない、ブラウザ指向のJavaScriptライブラリです。つまり、使用するには、ドキュメントのheadに<script>
タグを追加するだけで済みます。ビルドシステムは必要ありません。
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.js
をunpkg.comからダウンロードし、プロジェクトの適切なディレクトリに追加して、必要に応じて<script>
タグでインクルードします。
<script src="/path/to/htmx.min.js"></script>
npmスタイルのビルドシステムの場合、npm経由でhtmxをインストールできます。
npm install htmx.org@2.0.3
インストール後、node_modules/htmx.org/dist/htmx.js
(または.min.js
)を使用するには、適切なツールを使用する必要があります。たとえば、htmxをいくつかの拡張機能とプロジェクト固有のコードとバンドルすることができます。
webpackを使用してJavaScriptを管理している場合
htmx
をインストールします。index.js
にインポートを追加します。import 'htmx.org';
グローバルなhtmx
変数(推奨)を使用する場合は、ウィンドウスコープに挿入する必要があります。
index.js
にインポートします(手順2のインポートの下)。import 'path/to/my_custom.js';
window.htmx = require('htmx.org');
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リクエストは要素の「自然な」イベントによってトリガーされます。
input
、textarea
、およびselect
はchange
イベントでトリガーされます。form
はsubmit
イベントでトリガーされます。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-results
のdiv
に挿入します。
複数のトリガーは、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
にロードされることがわかります。
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を犠牲にして、スワップ操作中に既存のノードをその場で変更することにより、フォーカス、ビデオの状態などを維持するのに優れていることがよくあります。
モーフィングスタイルのスワップには、次の拡張機能が利用可能です。
新しい実験的なビュートランジション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
で使用可能な修飾子は次のとおりです。
オプション | 説明 |
---|---|
transition | true またはfalse 。このスワップにビュートランジションAPIを使用するかどうか。 |
swap | 古いコンテンツがクリアされてから新しいコンテンツが挿入されるまでのスワップ遅延(例:100ms )。 |
settle | 新しいコンテンツが挿入されてから確定するまでの確定遅延(例:100ms )。 |
ignoreTitle | true に設定されている場合、新しいコンテンツで見つかったタイトルは無視され、ドキュメントのタイトルは更新されません。 |
scroll | top またはbottom 。ターゲット要素をその上部または下部にスクロールします。 |
show | top または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
属性ページにあります。
htmxを使用すると、javascriptなしでCSSトランジションを簡単に使用できます。このHTMLコンテンツを考えてみましょう。
<div id="div1">Original Content</div>
このコンテンツがhtmxによってajaxリクエストを介してこの新しいコンテンツに置き換えられるとします。
<div id="div1" class="red">New Content</div>
2つのことに注意してください。
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アクセシビリティの推奨事項が適用されます。 例えば
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属性を使用して別の要素を指定できます。
注意: この要素はすべてのページに存在する必要があります。そうでない場合、履歴からの復元は正常に機能しません。
サードパーティライブラリを使用していて、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拡張機能を使用することを検討する価値があります。これにより、属性を介してレスポンスコードの動作を宣言的に設定できます。
クロスオリジンコンテキストでhtmxを使用する場合は、htmxヘッダーがクライアント側で表示されるように、WebサーバーがAccess-Controlヘッダーを設定するように構成してください。
htmxが実装するすべてのリクエストヘッダーとレスポンスヘッダーを参照してください。
htmxはリクエストに多くの便利なヘッダーを含んでいます
ヘッダー | 説明 |
---|---|
HX-Boosted | hx-boostを使用している要素を介したリクエストであることを示します |
HX-Current-URL | ブラウザの現在のURL |
HX-History-Restore-Request | ローカル履歴キャッシュでミスが発生した後の履歴復元のリクエストの場合は「true」 |
HX-Prompt | hx-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
クラスが適切な要素に適用されますhtmx-swapping
クラスでマークされますhtmx-swapping
クラスがターゲットから削除されますhtmx-added
クラスが新しいコンテンツの各部分に追加されますhtmx-settling
クラスがターゲットに適用されます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開発チームによってサポートされているいくつかの「コア」拡張機能をサポートしています。
morph
スワップ戦略をサポートします404
)に基づいて要素をターゲットにすることができます利用可能なすべての拡張機能は、拡張機能ページで確認できます。
独自の拡張機能を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は次のものをインストールするデモスクリプトサイトをホストしています。
デモ/フィドル/何でもに次のスクリプトタグを追加するだけです。
<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とよく組み合わせるスクリプトソリューションには、次のものがあります。
私たちの本には、「クライアントサイドスクリプティング」というタイトルの章全体があり、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は、まさにこの理由から、すべてのイベントをキャメルケースとケバブケースの両方でディスパッチします。
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>
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
ヘッダーを使用できない(または使用したくない)場合は、代わりに設定パラメータgetCacheBusterParam
をtrue
に設定できます。この設定変数が設定されている場合、htmxは、行うGET
リクエストにキャッシュバスターパラメータを含めます。これにより、ブラウザがhtmxベースのレスポンスとhtmxベースでないレスポンスを同じキャッシュスロットにキャッシュすることを防ぎます。
htmxは、ETag
とも期待どおりに動作します。サーバーが同じURLに対して異なるコンテンツをレンダリングできる場合(たとえば、HX-Request
ヘッダーの値に応じて)、サーバーは各コンテンツに対して異なるETag
を生成する必要があることに注意してください。
htmxを使用すると、DOMに直接ロジックを定義できます。これには多くの利点があり、最大の利点は挙動の局所性であり、システムの理解と保守が容易になります。
ただし、このアプローチに関する懸念事項はセキュリティです。htmxはHTMLの表現力を高めるため、悪意のあるユーザーがアプリケーションにHTMLを挿入できる場合、htmxのこの表現力を悪用して悪意のある目的を達成する可能性があります。
HTMLベースのWeb開発の最初のルールは、常に次のとおりです。ユーザーからの入力を信頼しないでください。サイトに挿入されるすべてのサードパーティの信頼できないコンテンツをエスケープする必要があります。これは、とりわけ、クロスサイトスクリプティング攻撃を防ぐためです。
XSSとその防止方法に関する広範なドキュメントは、優れたOWASP Webサイトにあり、クロスサイトスクリプティング防止チートシートが含まれています。
良いニュースは、これは非常に古く、よく理解されているトピックであり、サーバー側のテンプレート言語の大部分は、まさにそのような問題を防ぐためにコンテンツの自動エスケープをサポートしていることです。
とはいえ、人々がより危険な方法でHTMLを挿入することを選択する場合があり、多くの場合、テンプレート言語の何らかのraw()
メカニズムを介して行われます。これは正当な理由で行うことができますが、挿入されるコンテンツがサードパーティからのものである場合、hx-
およびdata-hx
で始まる属性、インライン<script>
タグなどを削除するなど、必ずスクラブする必要があります。
生のHTMLを挿入し、独自のエスケープを行っている場合、許可しない属性とタグをブラックリストに登録するのではなく、許可する属性とタグをホワイトリストに登録するのがベストプラクティスです。
もちろん、バグは発生し、開発者は完璧ではないため、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();
}
});
ブラウザは、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.config.historyEnabled | デフォルトはtrue 、実際にはテストにのみ役立ちます |
htmx.config.historyCacheSize | デフォルトは10 |
htmx.config.refreshOnHistoryMiss | デフォルトはfalse 、true に設定すると、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.globalViewTransitions | true に設定すると、htmxは新しいコンテンツをスワップするときにView Transition APIを使用します。 |
htmx.config.methodsThatUseUrlParams | デフォルトは["get", "delete"] 、htmxはリクエストボディではなくURLにパラメータをエンコードすることにより、これらのメソッドでリクエストをフォーマットします |
htmx.config.selfRequestsOnly | デフォルトはtrue 、現在のドキュメントと同じドメインへのAJAXリクエストのみを許可するかどうか |
htmx.config.ignoreTitle | デフォルトはfalse 、true に設定すると、新しいコンテンツでtitle タグが見つかった場合、htmxはドキュメントのタイトルを更新しません |
htmx.config.disableInheritance | htmxの属性継承を無効にし、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をお楽しみください! たくさんのことを、たくさんのコードを書かずに達成できます!