htmx の基本


htmx とは

htmx は、HTML の属性だけで Ajax や部分更新、履歴管理といった高度なインタラクションを実現できる軽量な JavaScript ライブラリです。JavaScript コードをほとんど書かずに、既存の HTML 要素に hx- から始まる属性を付与するだけで機能を追加できるのが特徴です。

a-blog cms には Ver. 1.3(2011年リリース)から、POST リクエストで部分的に HTML を取得・表示できる post include 機能が存在しました。この機能により、ページの一部だけをサーバーから取得して差し替える仕組みを長く提供してきましたが、利用できるのは POST メソッドのみでした。

今回 a-blog cms Ver. 3.2 からは、この post include の発想を引き継ぎつつ、より柔軟でオープンな標準技術である htmx を採用しました。htmx では hx-post だけでなく、hx-get をはじめとする多様な HTTP メソッドを利用でき、より多くのユースケースに対応できます。これにより、長年推奨してきた post include 機能から、モダンで汎用的な htmx への移行を推奨しています。

主な特徴

  • シンプルな記述: hx-gethx-post などの属性を HTML に追加するだけで、非同期通信やコンテンツの差し替えが可能です。

  • サーバーサイドと相性が良い: 部分的な HTML(フラグメント)をサーバーから返すことで、複雑な JavaScript フレームワークを使わずに SPA ライクな体験を実現できます。

  • プログレッシブエンハンスメント: htmx を使ったページは、JavaScript が無効でも通常のリンクやフォームとして動作します。


基本構文の例

リンクで部分更新 : hx-get

<a href="/news/page/2"
   hx-get="/news/page/2/tpl/include/entry/news-list.html"
   hx-target="#news-list"
   hx-swap="innerHTML">
  もっと見る
</a>

<ul id="news-list">
  <!-- モジュールで出力された記事一覧 -->
</ul>

この例では、リンクをクリックすると /news/page/2GET リクエストを送り、 include/entry/news-list.html のテンプレートを利用し、news のカテゴリーの page/2 の結果の HTML を #news-list の中身に置き換えます。ページ全体のリロードは行われません。

フォーム送信で部分更新 : hx-post

<form hx-post=""
      hx-target="#search-result"
      hx-swap="outerHTML">
  <input type="hidden" name="tpl" value="/include/entry/search-result.html">
  <input type="text" name="keyword" placeholder="キーワード">
  <button type="submit" name="ACMS_POST_2GET_Ajax">検索</button>
</form>

<div id="search-result">
  <!-- 送信結果がここに表示される -->
</div>

この例では、フォーム送信時に POST リクエストをサーバーに送り、include/entry/search-result.html のテンプレートを利用し、キーワード検索結果の HTMLを #search-result 全体と置き換えます。


事前準備について

Ver. 3.2 からは不要に

a-blog cms Ver. 3.2 では、標準の 組み込み JavaScript に htmx が含まれるようになりました。これにより、HTML 要素に hx-gethx-post の属性を記述するだけで自動的にライブラリが読み込まれるため、事前の設定は必要ありません。

なお、JavaScript などで htmx の属性を後から付加する場合、ライブラリが読み込まれないタイミングが発生する可能性があります。そのような場合は、以下の <meta> タグをあらかじめ入れておくことで対応できます。

<meta name="acms-htmx" content="enable">

Ver. 3.1 までは、htmx.org のドキュメントに沿って <head> 内でライブラリを読み込み、設定を記述する必要があります。例えば以下のような準備が必要でした。参考として共有しておきます。

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

<script>
  htmx.config.historyCacheSize = -1;
  htmx.config.refreshOnHistoryMiss = true;

  addEventListener('htmx:beforeHistoryUpdate', function (event) {
    const proposedUrl = event.detail.history.path;
    let customUrl = proposedUrl;
    if (proposedUrl.includes('/include/htmx/')) {
        customUrl = proposedUrl.replace(/\/include\/htmx\/.*\.html/, '');
    }
    event.detail.history.path = customUrl;
  });

  document.addEventListener("htmx:configRequest", function(event) {
      const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
      event.detail.headers['X-CSRF-Token'] = csrfToken;
  });

  addEventListener('htmx:afterSwap', function (event) {
    ACMS.Dispatch(event.target);
  });
</script>

htmx の設定

読み込み戦略(htmx_load_strategy)

htmx を利用したページ内リンクで、ブラウザの「戻る」「進む」ボタンを使った際に画面が更新されないことがあります(htmx 2 系の既知の仕様)。Ver. 3.2.29 以上の新規インストールでは、この問題を回避するため htmx_load_strategy: static がデフォルトで設定されています。

static 読み込みでは、サーバー側が組み込みJSのアセットとして htmx 本体・a-blog cmsで利用する場合の設定を配信します。hx-push-url を使ったページ内リンクでも、ブラウザの「戻る」「進む」ボタンが正しく機能します。

設定は private/config.system.yaml にあります(テーマ側の /js/config.js とは別の、サーバー側の設定ファイルです)。

htmx_load_strategy: static

また、htmx_load_strategy: off を指定することで、htmx  をcms 側で自動挿入する機能を無効にすることができます。

htmx_load_strategy: off

Ver. 3.2.28 以前から運用しているサイトをアップグレードした場合、private/config.system.yaml はアップデート時に書き換えられないため、htmx_load_strategy の設定が無いまま dynamic(従来の読み込み方式)で動作し続けます。dynamic 読み込みでは、組み込みJSバンドル側が実行時に htmx を初期化しますが、この方式は非推奨となり、ブラウザのコンソールに警告が表示されるようになりました。hx-push-url を使用している場合は、上記の設定を private/config.system.yaml に追記して static に切り替えることをおすすめします。

設定のカスタマイズ(static の場合)

新規インストールでは static 読み込みが既定のため、historyCacheSize ・ refreshOnHistoryMiss など htmx.config のオプションは、meta タグで制御できます。

<meta name="htmx-config" content='{"globalViewTransitions":true}'>

もしくは、private/config.system.yamlhtmx_config に、htmx.config のオプションをJSON文字列で指定します。

htmx_config: '{"historyCacheSize": 20, "refreshOnHistoryMiss": false}'

PHP側で動的に変えたい場合は、 extendsHtmxConfigフックでも上書きできます。

なお、eval() を許可する allowEval は常に無効化され、この方法でも上書きできません(詳しくは以下の「セキュリティ」を参照してください)。

利用可能な設定は htmx の公式ドキュメントを参照してください。

設定のカスタマイズ(dynamic の場合)

こちらの方法は非推奨です。Ver. 3.3 では医師予定です。

htmx_load_strategy: static を利用ください。

Ver. 3.2.28 以前からアップグレードしたサイトなど、dynamic 読み込みのままの場合、この機能の設定は /js/config.js にあります。設定を変える場合は、適用しているテーマ内にJavaScriptファイルを別途作成してください。詳しくは「組み込みJSについて:設定を編集する」を参照してください。

//------
// htmx
htmxMark: '[data-hx-get],[data-hx-post],[hx-get],[hx-post]', // htmxを有効にする要素のセレクタ
htmxConfig: {
  historyCacheSize: -1, // ローカルストレージにHTMLをキャッシュしない(キャッシュすると戻る・進むが正常に動作しないため)
  refreshOnHistoryMiss: true, // キャッシュがなければページを再読込
},

設定をカスタマイズする場合、JavaScriptファイルに下記のように記述します。

ACMS.Ready(function() {
  ACMS.Config.htmxConfig.globalViewTransitions = true;
});

この方法は dynamic 読み込み時のみ反映されます。static 読み込み時(新規インストールの既定)は、上記の「設定のカスタマイズ(static の場合)」を参照してください。

セキュリティ

htmxライブラリのオプションの中にもセキュリティを考慮したオプションが提供されております。その中で eval() を許可しない設定があり、こちらは htmxの設定でも上書きできないようになっているため、組み込みJS の htmx を使用する場合は以下の htmx機能は使用できません。

  • event filters

  • hx-on: attributes

  • hx-vals with the js: prefix

  • hx-headers with the js: prefix

公式ドキュメント > 設定オプション