T-CREATOR

htmx のデータ属性(hx-xxx)完全ガイド

htmx のデータ属性(hx-xxx)完全ガイド

htmx は従来のフロントエンド開発に革命をもたらしている軽量ライブラリですが、その真の力は豊富なデータ属性(hx-xxx)にあります。これらの属性を適切に理解し活用することで、JavaScript を書くことなく高度な Web アプリケーションを構築できるようになります。

本記事では、htmx の全データ属性を体系的に解説し、実際のコード例とエラーパターンも含めて、実務で即座に活用できる知識をお届けします。初心者の方でも段階的に理解できるよう、各属性の基本的な使い方から高度な組み合わせパターンまで幅広くカバーしています。

リクエスト系データ属性

リクエスト系属性は、htmx でサーバーとの通信を行う最も基本的な属性群です。これらをマスターすることで、フォーム送信から API コールまで幅広い処理を実現できます。

hx-get, hx-post, hx-put, hx-patch, hx-delete

基本的な使い方

htmx の中核となる HTTP メソッド属性です。それぞれが対応する HTTP リクエストを送信します。

javascript<!-- GET リクエストの基本形 -->
<button hx-get="/api/users" hx-target="#user-list">
  ユーザー一覧を取得
</button>

<!-- POST リクエストでデータ送信 -->
<form hx-post="/api/users" hx-target="#result">
  <input name="name" placeholder="ユーザー名" required />
  <input name="email" type="email" placeholder="メールアドレス" required />
  <button type="submit">ユーザー登録</button>
</form>

<!-- PUT リクエストで更新 -->
<button
  hx-put="/api/users/123"
  hx-include="#user-form"
  hx-target="#user-details">
  ユーザー情報を更新
</button>

<!-- DELETE リクエストで削除 -->
<button
  hx-delete="/api/users/123"
  hx-confirm="本当に削除しますか?"
  hx-target="#user-123"
  hx-swap="outerHTML">
  ユーザーを削除
</button>

実践的な活用例

検索機能での動的コンテンツ更新:

javascript<!-- リアルタイム検索の実装 -->
<input
  type="text"
  name="search"
  hx-get="/search"
  hx-trigger="keyup changed delay:300ms"
  hx-target="#search-results"
  placeholder="検索キーワードを入力..."
/>

<div id="search-results">
  <!-- 検索結果がここに表示される -->
</div>

よくあるエラーと対処法

エラー: 404 Not Found

csshtmx:responseError {detail: {xhr: XMLHttpRequest, error: "Not Found"}}

このエラーは指定した URL が存在しない場合に発生します:

javascript<!-- ❌ 間違った例 -->
<button hx-get="/api/user">データ取得</button>

<!-- ✅ 正しい例 -->
<button hx-get="/api/users">データ取得</button>

エラー: 405 Method Not Allowed

sqlResponse status: 405 Method Not Allowed
Error: The method GET is not allowed for the requested URL

サーバー側で対応していない HTTP メソッドを使用した場合のエラー:

javascript// サーバー側(Express.js)で適切なメソッドを定義
app.get('/api/users', (req, res) => {
  // GET リクエストの処理
});

app.post('/api/users', (req, res) => {
  // POST リクエストの処理
});

app.put('/api/users/:id', (req, res) => {
  // PUT リクエストの処理
});

app.delete('/api/users/:id', (req, res) => {
  // DELETE リクエストの処理
});

hx-boost

基本概念

hx-boost は既存のリンクやフォームを Ajax 化する属性です。ページ全体をリロードせずに、指定した部分のみを更新できます。

javascript<!-- 通常のリンクをAjax化 -->
<a href="/about" hx-boost="true">会社概要</a>

<!-- フォーム全体をAjax化 -->
<form action="/contact" method="post" hx-boost="true">
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">送信</button>
</form>

段階的な移行パターン

既存の Web サイトを段階的に SPA 風にする実例:

javascript<!-- Step 1: ナビゲーションのみAjax化 -->
<nav hx-boost="true" hx-target="body">
  <a href="/">ホーム</a>
  <a href="/products">商品一覧</a>
  <a href="/contact">お問い合わせ</a>
</nav>

<!-- Step 2: 特定のフォームもAjax化 -->
<form
  action="/newsletter"
  method="post"
  hx-boost="true"
  hx-target="#message"
  hx-swap="innerHTML">
  <input name="email" type="email" placeholder="メールアドレス" />
  <button type="submit">メルマガ登録</button>
</form>

<div id="message"></div>

エラーハンドリング

javascript<div hx-boost="true" hx-on:htmx:responseError="handleBoostError(event)">
  <a href="/admin">管理画面</a>
</div>

<script>
function handleBoostError(event) {
  const status = event.detail.xhr.status;

  switch(status) {
    case 401:
      alert('ログインが必要です');
      window.location.href = '/login';
      break;
    case 403:
      alert('アクセス権限がありません');
      break;
    case 404:
      alert('ページが見つかりません');
      break;
    default:
      alert('エラーが発生しました');
  }
}
</script>

hx-params

基本的な使い方

hx-params は送信するパラメータを細かく制御する属性です。フォームの一部のフィールドのみを送信したい場合に便利です。

javascript<!-- 全てのパラメータを送信(デフォルト) -->
<form hx-post="/api/users" hx-params="*">
  <input name="name" value="田中太郎" />
  <input name="email" value="tanaka@example.com" />
  <input name="phone" value="090-1234-5678" />
  <button type="submit">送信</button>
</form>

<!-- 特定のパラメータのみ送信 -->
<form hx-post="/api/users" hx-params="name,email">
  <input name="name" value="田中太郎" />
  <input name="email" value="tanaka@example.com" />
  <input name="phone" value="090-1234-5678" /> <!-- これは送信されない -->
  <button type="submit">送信</button>
</form>

<!-- 特定のパラメータを除外 -->
<form hx-post="/api/users" hx-params="not phone">
  <input name="name" value="田中太郎" />
  <input name="email" value="tanaka@example.com" />
  <input name="phone" value="090-1234-5678" /> <!-- これは送信されない -->
  <button type="submit">送信</button>
</form>

動的フォームでの活用

javascript<!-- ステップ形式のフォーム -->
<div id="step1">
  <form hx-post="/api/step1" hx-params="step1_*" hx-target="#step2">
    <input name="step1_name" placeholder="お名前" />
    <input name="step1_email" placeholder="メールアドレス" />
    <!-- 他のステップの入力値は送信されない -->
    <input name="step2_address" value="" style="display:none" />
    <button type="submit">次へ</button>
  </form>
</div>

<div id="step2" style="display:none">
  <!-- Step2 の内容 -->
</div>

ターゲティング系データ属性

ターゲティング系属性は、レスポンスをどこに、どのように表示するかを制御します。これらの属性を使いこなすことで、ページの任意の部分を動的に更新できます。

hx-target

基本的な使い方

hx-target は、レスポンスを表示する要素を指定します。CSS セレクターを使用して柔軟にターゲットを指定できます。

javascript<!-- ID セレクターでターゲット指定 -->
<button hx-get="/api/news" hx-target="#news-container">
  最新ニュースを取得
</button>
<div id="news-container">
  <!-- ニュースがここに表示される -->
</div>

<!-- クラスセレクターでターゲット指定 -->
<button hx-get="/api/weather" hx-target=".weather-info">
  天気情報を更新
</button>
<div class="weather-info">
  <!-- 天気情報がここに表示される -->
</div>

<!-- 複数の要素を対象にする -->
<button hx-get="/api/dashboard" hx-target=".dashboard-widget">
  ダッシュボードを更新
</button>
<div class="dashboard-widget">ウィジェット1</div>
<div class="dashboard-widget">ウィジェット2</div>

相対的なターゲット指定

javascript<!-- 親要素をターゲットに -->
<div class="card">
  <button hx-get="/api/card-content" hx-target="closest .card">
    カード内容を更新
  </button>
</div>

<!-- 次の兄弟要素をターゲットに -->
<button hx-get="/api/status" hx-target="next .status">
  ステータス更新
</button>
<div class="status">現在のステータス</div>

<!-- 前の兄弟要素をターゲットに -->
<div class="result"></div>
<button hx-get="/api/data" hx-target="previous .result">
  データ取得
</button>

エラーハンドリング

エラー: Target element not found

csshtmx:targetError {detail: {target: "#nonexistent", xhr: XMLHttpRequest}}
javascript<!-- ❌ 間違った例:存在しないIDを指定 -->
<button hx-get="/api/data" hx-target="#nonexistent">データ取得</button>

<!-- ✅ 正しい例:存在するターゲットを指定 -->
<button hx-get="/api/data" hx-target="#existing-element">データ取得</button>
<div id="existing-element"></div>

<!-- エラーハンドリング -->
<button
  hx-get="/api/data"
  hx-target="#result"
  hx-on:htmx:targetError="console.error('ターゲット要素が見つかりません')">
  データ取得
</button>

hx-select

基本的な使い方

hx-select は、レスポンス HTML の一部だけを抽出して表示する属性です。大きな HTML レスポンスから必要な部分だけを取得したい場合に便利です。

javascript<!-- レスポンスの特定部分のみを抽出 -->
<button
  hx-get="/full-page.html"
  hx-target="#content"
  hx-select="#main-content">
  メインコンテンツのみ取得
</button>

<!-- テーブルの特定行のみを抽出 -->
<button
  hx-get="/users-table.html"
  hx-target="#user-rows"
  hx-select="tbody tr">
  ユーザー行のみ更新
</button>

複数要素の選択

javascript<!-- 複数のセレクターで選択 -->
<button
  hx-get="/dashboard.html"
  hx-target="#widgets"
  hx-select=".widget-1, .widget-2, .widget-3">
  特定ウィジェットのみ更新
</button>

実際のレスポンス例

サーバーから返される HTML:

html<html>
  <head>
    <title>ユーザー一覧</title>
  </head>
  <body>
    <nav>ナビゲーション</nav>
    <main id="main-content">
      <h1>ユーザー一覧</h1>
      <div class="user-list">
        <div class="user">田中太郎</div>
        <div class="user">佐藤花子</div>
      </div>
    </main>
    <footer>フッター</footer>
  </body>
</html>

hx-select="#main-content" の場合、以下の部分のみが抽出されます:

html<main id="main-content">
  <h1>ユーザー一覧</h1>
  <div class="user-list">
    <div class="user">田中太郎</div>
    <div class="user">佐藤花子</div>
  </div>
</main>

hx-select-oob

基本的な使い方

hx-select-oob は、メインターゲット以外の要素も同時に更新する「Out of Band」更新を行う属性です。

javascript<!-- メインターゲット+追加要素の同時更新 -->
<button
  hx-get="/api/user-update"
  hx-target="#user-info"
  hx-select-oob="#notification, #user-count">
  ユーザー情報を更新
</button>

<div id="user-info"><!-- メインターゲット --></div>
<div id="notification"><!-- 同時に更新される --></div>
<div id="user-count"><!-- 同時に更新される --></div>

実用例:ショッピングカート

javascript<!-- 商品をカートに追加 -->
<button
  hx-post="/api/cart/add"
  hx-vals='{"product_id": "123"}'
  hx-target="#cart-items"
  hx-select-oob="#cart-count, #cart-total">
  カートに追加
</button>

<!-- カートの各部分 -->
<div id="cart-items"><!-- カート商品一覧 --></div>
<span id="cart-count">0</span><!-- 商品数 -->
<span id="cart-total">¥0</span><!-- 合計金額 -->

イベント・トリガー系データ属性

イベント・トリガー系属性は、いつ、どのようなイベントでリクエストを発生させるかを制御します。これらを使いこなすことで、豊富なユーザーインタラクションを実現できます。

hx-trigger

基本的なイベント

hx-trigger は、リクエストを発生させるイベントを指定します。

javascript<!-- クリックイベント(デフォルト) -->
<button hx-get="/api/data">データ取得</button>

<!-- マウスオーバーでトリガー -->
<div hx-get="/api/preview" hx-trigger="mouseenter" hx-target="#preview">
  マウスを乗せてプレビュー
</div>

<!-- フォーカス時にトリガー -->
<input
  hx-get="/api/suggestions"
  hx-trigger="focus"
  hx-target="#suggestions"
  placeholder="入力してください" />

<!-- 入力変更時にトリガー -->
<input
  hx-get="/api/search"
  hx-trigger="keyup changed delay:300ms"
  hx-target="#results"
  placeholder="検索キーワード" />

複数イベントの指定

javascript<!-- 複数のイベントでトリガー -->
<input
  hx-get="/api/validate"
  hx-trigger="blur, keyup delay:500ms"
  hx-target="#validation"
  placeholder="メールアドレス" />

<!-- 条件付きトリガー -->
<input
  hx-get="/api/search"
  hx-trigger="keyup[target.value.length > 2] delay:300ms"
  hx-target="#results"
  placeholder="3文字以上で検索" />

時間ベースのトリガー

javascript<!-- 定期的な更新 -->
<div
  hx-get="/api/status"
  hx-trigger="every 5s"
  hx-target="#status">
  ステータス(5秒毎に更新)
</div>

<!-- ページロード時 -->
<div
  hx-get="/api/initial-data"
  hx-trigger="load"
  hx-target="#content">
  初期データを読み込み中...
</div>

<!-- 一定時間後に実行 -->
<div
  hx-get="/api/delayed-content"
  hx-trigger="load delay:2s"
  hx-target="#delayed">
  2秒後にコンテンツを読み込み...
</div>

よくあるエラーと対処法

エラー: Invalid trigger syntax

csshtmx:parseError {detail: "Invalid trigger: 'keyup delay 300ms'"}
javascript<!-- ❌ 間違った例:コロンが抜けている -->
<input hx-trigger="keyup delay 300ms" />

<!-- ✅ 正しい例:適切な構文 -->
<input hx-trigger="keyup delay:300ms" />

hx-on (イベントハンドラー)

基本的な使い方

hx-on は、htmx のイベントに対して JavaScript を実行する属性です。

javascript<!-- リクエスト前の処理 -->
<button
  hx-get="/api/data"
  hx-on:htmx:beforeRequest="console.log('リクエスト開始')">
  データ取得
</button>

<!-- レスポンス後の処理 -->
<form
  hx-post="/api/contact"
  hx-on:htmx:afterRequest="this.reset()">
  <input name="message" />
  <button type="submit">送信</button>
</form>

<!-- エラー時の処理 -->
<button
  hx-get="/api/data"
  hx-on:htmx:responseError="alert('エラーが発生しました')">
  データ取得
</button>

実用的なイベントハンドリング

javascript<!-- フォーム送信時のバリデーション -->
<form
  hx-post="/api/users"
  hx-on:htmx:beforeRequest="return validateForm(this)">
  <input name="email" type="email" required />
  <button type="submit">登録</button>
</form>

<script>
function validateForm(form) {
  const email = form.querySelector('[name="email"]').value;

  if (!email.includes('@')) {
    alert('有効なメールアドレスを入力してください');
    return false; // リクエストをキャンセル
  }

  return true; // リクエストを続行
}
</script>

<!-- アニメーション付きの更新 -->
<button
  hx-get="/api/content"
  hx-target="#content"
  hx-on:htmx:beforeSwap="fadeOut(event.target)"
  hx-on:htmx:afterSwap="fadeIn(event.target)">
  コンテンツ更新
</button>

<script>
function fadeOut(element) {
  element.style.opacity = '0.5';
}

function fadeIn(element) {
  element.style.opacity = '1';
}
</script>

hx-encoding

基本的な使い方

hx-encoding は、リクエストのエンコーディング方式を指定します。ファイルアップロードでは必須の属性です。

javascript<!-- 通常のフォーム送信(デフォルト) -->
<form hx-post="/api/contact">
  <input name="name" />
  <input name="email" />
  <button type="submit">送信</button>
</form>

<!-- ファイルアップロード -->
<form
  hx-post="/api/upload"
  hx-encoding="multipart/form-data"
  hx-target="#result">
  <input name="file" type="file" />
  <button type="submit">アップロード</button>
</form>

<!-- JSONデータの送信 -->
<form
  hx-post="/api/json-data"
  hx-encoding="application/json"
  hx-target="#result">
  <input name="title" />
  <textarea name="content"></textarea>
  <button type="submit">JSON送信</button>
</form>

ファイルアップロードの実装例

javascript<!-- プログレスバー付きファイルアップロード -->
<form
  hx-post="/api/upload"
  hx-encoding="multipart/form-data"
  hx-target="#upload-result"
  hx-indicator="#upload-progress">

  <input name="file" type="file" accept="image/*" required />
  <button type="submit">画像をアップロード</button>

  <div id="upload-progress" class="htmx-indicator">
    アップロード中...
  </div>
</form>

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

エラーハンドリング

javascript<!-- ファイルサイズ制限のエラー -->
<form
  hx-post="/api/upload"
  hx-encoding="multipart/form-data"
  hx-on:htmx:responseError="handleUploadError(event)">

  <input name="file" type="file" />
  <button type="submit">アップロード</button>
</form>

<script>
function handleUploadError(event) {
  const status = event.detail.xhr.status;

  switch(status) {
    case 413:
      alert('ファイルサイズが大きすぎます(最大5MB)');
      break;
    case 415:
      alert('サポートされていないファイル形式です');
      break;
    default:
      alert('アップロードに失敗しました');
  }
}
</script>

レスポンス処理系データ属性

レスポンス処理系属性は、サーバーからのレスポンスをどのように処理し、DOM に反映するかを制御します。これらの属性を使いこなすことで、動的な Web アプリケーションの核心部分を実現できます。

hx-swap

基本的なスワップ方法

hx-swap は、レスポンス HTML をターゲット要素にどのように挿入するかを指定します。

javascript<!-- 内容を置き換え(デフォルト) -->
<button hx-get="/api/content" hx-target="#content" hx-swap="innerHTML">
  コンテンツを更新
</button>

<!-- 要素全体を置き換え -->
<div id="user-card">
  <button hx-get="/api/user/123" hx-target="#user-card" hx-swap="outerHTML">
    ユーザー情報を更新
  </button>
</div>

<!-- 前に挿入 -->
<div id="message-list">
  <button hx-get="/api/messages/new" hx-target="#message-list" hx-swap="afterBegin">
    新しいメッセージを追加
  </button>
</div>

<!-- 後に挿入 -->
<div id="comment-list">
  <button hx-get="/api/comments/load" hx-target="#comment-list" hx-swap="beforeEnd">
    コメントを読み込み
  </button>
</div>

スワップのタイミング制御

javascript<!-- スワップ前にアニメーション -->
<button
  hx-get="/api/content"
  hx-target="#content"
  hx-swap="innerHTML settle:100ms">
  フェードイン効果で更新
</button>

<!-- スワップ後に遅延処理 -->
<button
  hx-get="/api/notification"
  hx-target="#notification"
  hx-swap="innerHTML swap:500ms">
  通知を表示
</button>

スクロール制御

javascript<!-- トップにスクロール -->
<button
  hx-get="/api/content"
  hx-target="#content"
  hx-swap="innerHTML scroll:top">
  ページトップへ
</button>

<!-- 新しい要素にスクロール -->
<button
  hx-get="/api/new-item"
  hx-target="#item-list"
  hx-swap="beforeEnd scroll:bottom">
  アイテム追加
</button>

hx-swap-oob

基本的な使い方

hx-swap-oob は、メインターゲット以外の要素を「Out of Band」で更新する属性です。

javascript<!-- 複数箇所の同時更新 -->
<button
  hx-post="/api/cart/add"
  hx-vals='{"product_id": "123"}'
  hx-target="#cart-summary"
  hx-swap-oob="#cart-count,#cart-total">
  カートに追加
</button>

サーバーサイドでの OOB 実装

Express.js での実装例:

javascript// サーバーサイド(Express.js)
app.post('/api/cart/add', (req, res) => {
  const productId = req.body.product_id;

  // カートにアイテムを追加
  cart.addItem(productId);

  // メインコンテンツ + OOB要素のHTMLを返す
  const html = `
    <!-- メインターゲット用のHTML -->
    <div>商品が追加されました</div>
    
    <!-- OOB更新用のHTML -->
    <span id="cart-count" hx-swap-oob="true">${cart.itemCount}</span>
    <span id="cart-total" hx-swap-oob="true">¥${cart.total}</span>
  `;

  res.send(html);
});

実際のエラーパターン

エラー: OOB element not found

csshtmx:oobError {detail: {oobElement: "#nonexistent-element"}}
javascript<!-- ❌ 間違った例:存在しない要素を指定 -->
<button hx-get="/api/data" hx-swap-oob="#nonexistent">更新</button>

<!-- ✅ 正しい例:存在する要素を指定 -->
<button hx-get="/api/data" hx-swap-oob="#existing-element">更新</button>
<div id="existing-element">更新される要素</div>

hx-preserve

基本的な使い方

hx-preserve は、DOM 更新時に特定の要素を保持する属性です。フォームの入力状態やスクロール位置を維持したい場合に使用します。

javascript<!-- 入力中のフォームを保持 -->
<div hx-get="/api/content" hx-target="#main-content">
  <form hx-preserve="true">
    <input name="search" placeholder="検索中の内容を保持" />
    <button type="submit">検索</button>
  </form>
</div>

<!-- 動画プレーヤーの再生状態を保持 -->
<video hx-preserve="true" controls>
  <source src="/video.mp4" type="video/mp4">
</video>

ID ベースの保持

javascript<!-- 特定のIDを持つ要素を保持 -->
<div hx-get="/api/dashboard" hx-target="#dashboard">
  <div id="chat-widget" hx-preserve="true">
    <!-- チャットの状態を保持 -->
    <div class="chat-messages">
      <!-- メッセージ履歴 -->
    </div>
    <input type="text" placeholder="メッセージを入力..." />
  </div>
</div>

hx-disinherit

基本的な使い方

hx-disinherit は、親要素の htmx 属性を継承しないようにする属性です。

javascript<!-- 親要素のhx-boostを無効化 -->
<div hx-boost="true">
  <a href="/page1">Ajax化されるリンク</a>
  <a href="/page2" hx-disinherit="hx-boost">通常のリンク</a>
</div>

<!-- 親要素の複数属性を無効化 -->
<form hx-post="/api/form" hx-target="#result">
  <input name="field1" />
  <input name="field2" />
  <button type="submit">送信</button>

  <!-- この部分は親の設定を無効化 -->
  <div hx-disinherit="hx-post hx-target">
    <button hx-get="/api/other">別のAPI</button>
  </div>
</form>

UI・UX 系データ属性

UI・UX 系属性は、ユーザーインターフェースとユーザーエクスペリエンスを向上させるための属性群です。ローディング表示やユーザーフィードバックの制御に使用します。

hx-indicator

基本的な使い方

hx-indicator は、リクエスト中に表示するローディング要素を指定します。

javascript<!-- デフォルトインジケーター -->
<button hx-get="/api/data" hx-target="#result">
  データ取得
</button>
<div id="result">
  <div class="htmx-indicator">読み込み中...</div>
</div>

<!-- カスタムインジケーター -->
<button
  hx-get="/api/slow-data"
  hx-target="#result"
  hx-indicator="#loading-spinner">
  時間のかかる処理
</button>

<div id="loading-spinner" class="htmx-indicator">
  <div class="spinner"></div>
  <p>データを処理中です...</p>
</div>

CSS と組み合わせた実装

css/* インジケーターのスタイル */
.htmx-indicator {
  display: none;
}

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

.spinner {
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  width: 30px;
  height: 30px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

複数のインジケーター

javascript<!-- フォーム送信用インジケーター -->
<form
  hx-post="/api/contact"
  hx-target="#form-result"
  hx-indicator="#form-loading">

  <input name="email" type="email" required />
  <textarea name="message" required></textarea>

  <button type="submit">
    送信
    <span id="form-loading" class="htmx-indicator">送信中...</span>
  </button>
</form>

<!-- ファイルアップロード用インジケーター -->
<form
  hx-post="/api/upload"
  hx-encoding="multipart/form-data"
  hx-indicator="#upload-progress">

  <input name="file" type="file" />
  <button type="submit">アップロード</button>

  <div id="upload-progress" class="htmx-indicator">
    <div class="progress-bar">
      <div class="progress-fill"></div>
    </div>
    <p>ファイルをアップロード中...</p>
  </div>
</form>

hx-disable

基本的な使い方

hx-disable は、リクエスト中に要素を無効化する属性です。

javascript<!-- ボタンを無効化 -->
<button hx-post="/api/submit" hx-disable="true">
  送信(連続クリック防止)
</button>

<!-- フォーム全体を無効化 -->
<form hx-post="/api/form" hx-disable="true">
  <input name="field1" />
  <input name="field2" />
  <button type="submit">送信</button>
</form>

カスタム無効化

javascript<!-- 特定の要素のみ無効化 -->
<div>
  <button
    hx-post="/api/data"
    hx-disable="#submit-btn, #cancel-btn">
    データ送信
  </button>

  <button id="submit-btn">送信</button>
  <button id="cancel-btn">キャンセル</button>
</div>

hx-loading-states

基本的な使い方

hx-loading-states は、htmx がリクエスト中に要素に追加する CSS クラスを制御します。

javascript<!-- カスタムローディングクラス -->
<button
  hx-get="/api/data"
  hx-loading-states="loading">
  データ取得
</button>

CSS での状態制御

css/* 通常の状態 */
.data-button {
  background-color: #007bff;
  color: white;
}

/* ローディング中の状態 */
.data-button.loading {
  background-color: #6c757d;
  cursor: not-allowed;
  opacity: 0.7;
}

.data-button.loading::after {
  content: ' 読み込み中...';
}

hx-sync

基本的な使い方

hx-sync は、複数のリクエストの同期を制御する属性です。

javascript<!-- 同じ要素からのリクエストを順序実行 -->
<button
  hx-get="/api/data"
  hx-sync="this:queue">
  データ取得(順序実行)
</button>

<!-- 同じ要素からのリクエストをキャンセル -->
<input
  hx-get="/api/search"
  hx-trigger="keyup changed delay:300ms"
  hx-sync="this:abort"
  placeholder="検索キーワード" />

<!-- グループ単位でのリクエスト制御 -->
<div class="user-actions">
  <button
    hx-post="/api/user/save"
    hx-sync="closest .user-actions:queue">
    保存
  </button>
  <button
    hx-post="/api/user/delete"
    hx-sync="closest .user-actions:queue">
    削除
  </button>
</div>

実用例:検索機能での重複リクエスト防止

javascript<!-- 前のリクエストをキャンセルして新しいリクエストを実行 -->
<input
  hx-get="/api/search"
  hx-trigger="keyup changed delay:300ms"
  hx-target="#search-results"
  hx-sync="this:abort"
  hx-indicator="#search-loading"
  hx-vals="js:getSearchContext()"
  placeholder="検索キーワードを入力..." />

<div id="search-loading" class="htmx-indicator">検索中...</div>
<div id="search-results"></div>

<script>
function getSearchContext() {
  return {
    category: document.querySelector('#category-filter').value,
    price_range: document.querySelector('#price-range').value,
    timestamp: Date.now()
  };
}
</script>

エラーハンドリング

javascript<!-- 同期エラーの処理 -->
<button
  hx-post="/api/process"
  hx-sync="this:queue"
  hx-on:htmx:sendError="handleSyncError(event)">
  処理実行
</button>

<script>
function handleSyncError(event) {
  console.error('同期エラー:', event.detail);
  alert('リクエストの処理中にエラーが発生しました');
}
</script>

ヘッダー・リクエスト制御系データ属性

ヘッダー・リクエスト制御系属性は、HTTP リクエストの詳細な制御を行う属性群です。認証情報の送信やカスタムデータの追加など、より高度な Web API との連携を実現できます。

hx-headers

基本的な使い方

hx-headers は、リクエストにカスタムヘッダーを追加する属性です。

javascript<!-- 認証トークンの送信 -->
<button
  hx-get="/api/protected-data"
  hx-headers='{"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'>
  認証が必要なデータを取得
</button>

<!-- コンテンツタイプの指定 -->
<form
  hx-post="/api/json-endpoint"
  hx-headers='{"Content-Type": "application/json", "X-Custom-Header": "value"}'>
  <input name="data" />
  <button type="submit">JSON送信</button>
</form>

動的ヘッダーの設定

javascript<!-- JavaScript関数から動的にヘッダーを取得 -->
<button
  hx-get="/api/user-data"
  hx-headers="js:getAuthHeaders()">
  ユーザーデータ取得
</button>

<script>
function getAuthHeaders() {
  const token = localStorage.getItem('authToken');
  return {
    'Authorization': `Bearer ${token}`,
    'X-Request-ID': Date.now().toString(),
    'X-User-Agent': navigator.userAgent
  };
}
</script>

API キーの管理

javascript<!-- 環境変数からAPIキーを取得 -->
<button
  hx-get="/api/external-service"
  hx-headers="js:getApiHeaders()"
  hx-target="#api-result">
  外部API呼び出し
</button>

<script>
function getApiHeaders() {
  return {
    'X-API-Key': process.env.API_KEY || 'development-key',
    'Content-Type': 'application/json'
  };
}
</script>

hx-include

基本的な使い方

hx-include は、リクエストに追加のフォーム要素を含める属性です。

javascript<!-- 他のフォーム要素を含める -->
<form id="main-form">
  <input name="title" placeholder="タイトル" />
  <textarea name="content" placeholder="内容"></textarea>
</form>

<form id="meta-form">
  <input name="category" placeholder="カテゴリ" />
  <input name="tags" placeholder="タグ" />
</form>

<button
  hx-post="/api/articles"
  hx-include="#main-form, #meta-form">
  記事を投稿
</button>

条件付きフィールド

javascript<!-- ユーザーの選択に応じて含めるフィールドを変更 -->
<form id="user-form">
  <select name="user_type" onchange="updateIncludes(this.value)">
    <option value="basic">基本ユーザー</option>
    <option value="premium">プレミアムユーザー</option>
  </select>
</form>

<div id="basic-fields" style="display:none">
  <input name="basic_field" placeholder="基本フィールド" />
</div>

<div id="premium-fields" style="display:none">
  <input name="premium_field" placeholder="プレミアムフィールド" />
  <input name="priority" type="number" placeholder="優先度" />
</div>

<button
  id="submit-btn"
  hx-post="/api/users"
  hx-include="#user-form, #basic-fields">
  ユーザー登録
</button>

<script>
function updateIncludes(userType) {
  const submitBtn = document.getElementById('submit-btn');

  if (userType === 'premium') {
    submitBtn.setAttribute('hx-include', '#user-form, #premium-fields');
    document.getElementById('premium-fields').style.display = 'block';
    document.getElementById('basic-fields').style.display = 'none';
  } else {
    submitBtn.setAttribute('hx-include', '#user-form, #basic-fields');
    document.getElementById('basic-fields').style.display = 'block';
    document.getElementById('premium-fields').style.display = 'none';
  }
}
</script>

hx-vals

基本的な使い方

hx-vals は、リクエストに追加の値を含める属性です。

javascript<!-- 静的な値を追加 -->
<button
  hx-post="/api/analytics"
  hx-vals='{"event": "button_click", "section": "header"}'>
  分析データ送信
</button>

<!-- 動的な値を追加 -->
<button
  hx-post="/api/user-action"
  hx-vals="js:getUserContext()">
  ユーザーアクション記録
</button>

<script>
function getUserContext() {
  return {
    timestamp: new Date().toISOString(),
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    screen_resolution: `${screen.width}x${screen.height}`,
    user_agent: navigator.userAgent
  };
}
</script>

フォームとの組み合わせ

javascript<!-- フォームデータに追加情報を付加 -->
<form
  hx-post="/api/feedback"
  hx-vals='{"source": "contact_form", "version": "2.1.0"}'>
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">フィードバック送信</button>
</form>

hx-confirm

基本的な使い方

hx-confirm は、リクエスト実行前に確認ダイアログを表示する属性です。

javascript<!-- 基本的な確認ダイアログ -->
<button
  hx-delete="/api/users/123"
  hx-confirm="本当にこのユーザーを削除しますか?">
  ユーザー削除
</button>

<!-- 重要な操作の確認 -->
<button
  hx-post="/api/orders/process"
  hx-confirm="注文を確定します。この操作は取り消せません。よろしいですか?"
  hx-target="#order-result">
  注文確定
</button>

カスタム確認処理

javascript<!-- JavaScript関数による確認処理 -->
<button
  hx-delete="/api/data/critical"
  hx-confirm="js:customConfirm()">
  重要データ削除
</button>

<script>
function customConfirm() {
  const userConfirmed = confirm('この操作は元に戻せません。');

  if (userConfirmed) {
    const secondConfirm = confirm('本当によろしいですか?');
    if (secondConfirm) {
      // 追加の確認処理
      const password = prompt('パスワードを入力してください:');
      return password === 'admin123';
    }
  }

  return false;
}
</script>

高度な制御系データ属性

高度な制御系属性は、SPA(Single Page Application)機能やブラウザ履歴の管理など、より複雑な Web アプリケーションの実現に必要な属性群です。

hx-history

基本的な使い方

hx-history は、ブラウザ履歴の管理を制御する属性です。

javascript<!-- 履歴に記録しない -->
<button
  hx-get="/api/modal-content"
  hx-target="#modal"
  hx-history="false">
  モーダル表示(履歴なし)
</button>

<!-- 特定の要素のスクロール位置を保存 -->
<div
  hx-get="/api/page-content"
  hx-target="#content"
  hx-history-elt="#content">
  ページ内容を更新
</div>

hx-push-url

基本的な使い方

hx-push-url は、リクエスト後にブラウザの URL を更新する属性です。

javascript<!-- URLをプッシュ -->
<button
  hx-get="/api/page/about"
  hx-target="#main-content"
  hx-push-url="/about">
  About ページ
</button>

<!-- 動的なURLプッシュ -->
<a
  hx-get="/api/products/123"
  hx-target="#product-detail"
  hx-push-url="true">
  商品詳細
</a>

SPA 風ナビゲーション

javascript<!-- ナビゲーションメニュー -->
<nav>
  <a
    href="/home"
    hx-get="/api/pages/home"
    hx-target="#main-content"
    hx-push-url="true">
    ホーム
  </a>
  <a
    href="/products"
    hx-get="/api/pages/products"
    hx-target="#main-content"
    hx-push-url="true">
    商品一覧
  </a>
  <a
    href="/contact"
    hx-get="/api/pages/contact"
    hx-target="#main-content"
    hx-push-url="true">
    お問い合わせ
  </a>
</nav>

<main id="main-content">
  <!-- ページコンテンツがここに表示される -->
</main>

hx-replace-url

基本的な使い方

hx-replace-url は、履歴に新しいエントリを追加せずに URL を更新する属性です。

javascript<!-- URLを置換(履歴に追加されない) -->
<form
  hx-post="/api/search"
  hx-target="#search-results"
  hx-replace-url="true">
  <input name="query" placeholder="検索キーワード" />
  <button type="submit">検索</button>
</form>

hx-ext

基本的な使い方

hx-ext は、htmx の拡張機能を有効にする属性です。

javascript<!-- JSON エンコーディング拡張 -->
<div hx-ext="json-enc">
  <form hx-post="/api/json-data">
    <input name="title" />
    <textarea name="content"></textarea>
    <button type="submit">JSON送信</button>
  </form>
</div>

<!-- WebSocket 拡張 -->
<div hx-ext="ws" ws-connect="/ws">
  <div id="chat-messages"></div>
  <form ws-send>
    <input name="message" placeholder="メッセージを入力..." />
    <button type="submit">送信</button>
  </form>
</div>

Server-Sent Events 拡張

javascript<!-- SSE 拡張を使用したリアルタイム更新 -->
<div
  hx-ext="sse"
  sse-connect="/events"
  sse-swap="message">
  <div id="live-updates">
    <!-- リアルタイム更新がここに表示される -->
  </div>
</div>

実践的な組み合わせパターン

実際の Web アプリケーション開発では、複数の htmx 属性を組み合わせて使用します。ここでは、よく使われる組み合わせパターンと、エラーハンドリング、パフォーマンス最適化の手法を紹介します。

よく使われる属性の組み合わせ

リアルタイム検索機能

javascript<!-- 高性能な検索機能の実装 -->
<input
  hx-get="/api/search"
  hx-trigger="keyup changed delay:300ms"
  hx-target="#search-results"
  hx-indicator="#search-loading"
  hx-sync="this:abort"
  hx-vals="js:getSearchContext()"
  placeholder="商品を検索..." />

<div id="search-loading" class="htmx-indicator">
  検索中...
</div>

<div id="search-results">
  <!-- 検索結果がここに表示される -->
</div>

<script>
function getSearchContext() {
  return {
    category: document.querySelector('#category-filter').value,
    price_range: document.querySelector('#price-range').value,
    timestamp: Date.now()
  };
}
</script>

モーダルダイアログシステム

javascript<!-- モーダル表示ボタン -->
<button
  hx-get="/api/modal/user-profile"
  hx-target="#modal-container"
  hx-swap="innerHTML"
  hx-trigger="click"
  hx-indicator="#modal-loading"
  hx-on:htmx:afterSwap="showModal()">
  プロフィール表示
</button>

<!-- モーダルコンテナ -->
<div id="modal-container"></div>
<div id="modal-loading" class="htmx-indicator">読み込み中...</div>

<script>
function showModal() {
  document.getElementById('modal-container').style.display = 'block';
  document.body.style.overflow = 'hidden';
}

function hideModal() {
  document.getElementById('modal-container').style.display = 'none';
  document.body.style.overflow = 'auto';
}
</script>

ショッピングカート機能

javascript<!-- 商品をカートに追加 -->
<button
  hx-post="/api/cart/add"
  hx-vals='{"product_id": "123", "quantity": "1"}'
  hx-target="#cart-summary"
  hx-swap="innerHTML"
  hx-swap-oob="#cart-count,#cart-total,#cart-items"
  hx-indicator="#add-to-cart-loading"
  hx-disable="true"
  hx-on:htmx:afterRequest="showCartNotification()">
  <span>カートに追加</span>
  <span id="add-to-cart-loading" class="htmx-indicator">追加中...</span>
</button>

<!-- カート表示エリア -->
<div id="cart-count">0</div>
<div id="cart-total">¥0</div>
<div id="cart-items"></div>
<div id="cart-summary"></div>

<script>
function showCartNotification() {
  const notification = document.createElement('div');
  notification.className = 'cart-notification';
  notification.textContent = '商品がカートに追加されました';
  document.body.appendChild(notification);

  setTimeout(() => {
    notification.remove();
  }, 3000);
}
</script>

エラーハンドリング時の属性活用

包括的なエラーハンドリング

javascript<!-- 複数のエラーケースに対応 -->
<form
  hx-post="/api/user/register"
  hx-target="#form-result"
  hx-indicator="#form-loading"
  hx-on:htmx:beforeRequest="clearErrors()"
  hx-on:htmx:responseError="handleFormError(event)"
  hx-on:htmx:sendError="handleNetworkError(event)"
  hx-on:htmx:timeout="handleTimeout()">

  <input name="email" type="email" required />
  <input name="password" type="password" required />
  <button type="submit">登録</button>

  <div id="form-loading" class="htmx-indicator">
    登録処理中...
  </div>
</form>

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

<script>
function clearErrors() {
  document.getElementById('form-result').innerHTML = '';
}

function handleFormError(event) {
  const status = event.detail.xhr.status;
  const response = event.detail.xhr.responseText;

  switch(status) {
    case 400:
      showError('入力内容に誤りがあります: ' + response);
      break;
    case 409:
      showError('そのメールアドレスは既に使用されています');
      break;
    case 422:
      showError('バリデーションエラー: ' + response);
      break;
    case 500:
      showError('サーバーエラーが発生しました。しばらく時間をおいて再度お試しください');
      break;
    default:
      showError('予期しないエラーが発生しました');
  }
}

function handleNetworkError(event) {
  showError('ネットワークエラーが発生しました。インターネット接続を確認してください');
}

function handleTimeout(event) {
  showError('リクエストがタイムアウトしました。再度お試しください');
}

function showError(message) {
  document.getElementById('form-result').innerHTML =
    `<div class="error-message">${message}</div>`;
}
</script>

パフォーマンス最適化の属性設定

効率的なデータ読み込み

javascript<!-- 仮想スクロール風の実装 -->
<div id="item-list">
  <!-- 初期アイテム -->
</div>

<div
  hx-get="/api/items/more"
  hx-trigger="revealed"
  hx-target="#item-list"
  hx-swap="beforeEnd"
  hx-indicator="#load-more-indicator"
  hx-vals="js:getLoadMoreParams()">

  <div id="load-more-indicator" class="htmx-indicator">
    さらに読み込み中...
  </div>
</div>

<script>
let currentPage = 1;

function getLoadMoreParams() {
  return {
    page: ++currentPage,
    limit: 20,
    timestamp: Date.now()
  };
}
</script>

キャッシュ制御とプリロード

javascript<!-- 戦略的なプリロード -->
<nav>
  <a
    href="/products"
    hx-get="/api/pages/products"
    hx-trigger="mouseenter once"
    hx-target="#preload-cache"
    hx-swap="none"
    onclick="loadProductsPage(event)">
    商品一覧
  </a>
</nav>

<div id="preload-cache" style="display:none"></div>
<main id="main-content"></main>

<script>
function loadProductsPage(event) {
  event.preventDefault();

  // プリロードされたコンテンツがあるかチェック
  const preloadedContent = document.querySelector('#preload-cache').innerHTML;

  if (preloadedContent) {
    document.getElementById('main-content').innerHTML = preloadedContent;
    history.pushState(null, '', '/products');
  } else {
    // フォールバック:通常のリクエスト
    htmx.ajax('GET', '/api/pages/products', '#main-content');
  }
}
</script>

まとめ

本記事では、htmx の全データ属性を体系的に解説してきました。これらの属性を適切に組み合わせることで、JavaScript を書くことなく高度な Web アプリケーションを構築できることをご理解いただけたでしょう。

重要なポイントの再確認

基本原則

  • シンプルさの追求: htmx は HTML の延長として動作し、学習コストを最小限に抑えます
  • 段階的な導入: 既存の Web サイトに少しずつ機能を追加できます
  • サーバーサイド重視: ロジックはサーバーサイドで処理し、クライアントはシンプルに保ちます

属性の分類と活用場面

分類主な属性適用場面
リクエスト系hx-get, hx-post, hx-put, hx-deleteAPI 通信、CRUD 操作
ターゲティング系hx-target, hx-select, hx-swapDOM 更新、部分更新
イベント・トリガー系hx-trigger, hx-onユーザーインタラクション
レスポンス処理系hx-swap, hx-swap-oob複雑な画面更新
UI・UX 系hx-indicator, hx-disableユーザビリティ向上
制御系hx-headers, hx-vals認証、カスタマイズ
高度な機能hx-history, hx-extSPA 機能、拡張

エラーハンドリングのベストプラクティス

実際の Web アプリケーション開発では、適切なエラーハンドリングが不可欠です:

javascript// よく発生するエラーと対策
- 404 Not FoundURLの確認とフォールバック処理
- 500 Internal Server Error → ユーザーフレンドリーなメッセージ表示
- Network Error → オフライン対応とリトライ機能
- Timeout → 適切なタイムアウト設定と進捗表示

パフォーマンス最適化の要点

  • リクエストの最適化: hx-sync でリクエストの重複を防ぐ
  • 部分更新の活用: hx-select で必要な部分のみを取得
  • プリロード戦略: hx-trigger="mouseenter" でユーザー体験を向上
  • キャッシュ制御: 適切な HTTP ヘッダーでパフォーマンスを向上

htmx は現代の Web 開発において、シンプルさと機能性の最適なバランスを提供します。従来の SPA フレームワークと比較して学習コストが低く、既存のサーバーサイド開発者でも容易に習得できる点が大きな魅力です。

今後の Web アプリケーション開発において、htmx の知識は確実にあなたの開発効率と品質の向上に貢献するでしょう。ぜひ実際のプロジェクトで活用してみてください。

関連リンク