✂️

htmzは何をしているか?

2024/02/24に公開

はじめに

htmzは、htmxへのアンサー的なマイクロフレームワークというかスニペットです。新たにDSLを覚える必要もなく、依存するJavaScriptライブラリもないのでbundleする必要もないです。

https://leanrada.com/htmz/

結論

隠しておいたiframeに読み込んだ内容で置き換えている。

仕組み

iframeをうまいこと使ってます。htmzのコードは以下のようになっています。

<script>
  function htmz(frame) {
    // setTimeoutで囲まないと、Chromeなどのブラウザでは、自動スクロールしてしまいます。
    setTimeout(() =>
      document
        .querySelector(frame.contentWindow.location.hash || null)
        ?.replaceWith(...frame.contentDocument.body.children)
    );
  }
</script>
<iframe hidden name="htmz" onload="window.htmz(this)"></iframe>

frame.contentWindow.location.hash は、iframeのURLのhash部分、つまり #hoge という様な文字列を取得します。これを querySelector に渡すことで、hashはselectorの役割をはたし、<div id="hoge"></div> というようなタグで定義されたElementが取得されます。あとは、取得したElementを、replaceWith により、 frame.contentDocument.body.children すなわちiframeの内容に、置き換えます。
これら置き換えの処理が、iframeの onload をきっかけに呼び出されるので、iframeになにか読み込まれるたびに、hashが指す要素がiframeの内容に置き換えられることになります。

簡単なサンプル

タブをクリックすることで画面が切り替わるようなものを考えます。htmzだと以下のようになります。

<div>
  <a href="page1.html#tab-panel" target="htmz">page1</a>
  <a href="page2.html#tab-panel" target="htmz">page2</a>
</div>
<div id="tab-panel">init</div>

私も長らく、targetを、別タブを開くための target="_blank" でしか使ってきませんでした。しかし、htmzのおかげで、iframeのnameを指定することで、目的のiframeに読み込むことができることを思い出しました。

aタグで target="htmz" とすることで、リンクがクリックされると name="htmz" なiframeに読み込まれ、iframeはonloadに定義したhtmzの置き換え処理が動き、href="page1.html#tab-panel" とhashで指定した <div id="tab-panel"> が変化します。

コードの全景は以下のようになります(Firefoxのコンソールで「Warningも出ないように」と思って書いてたら、ちょっとhtmzのサンプルより大げさになりました)。

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>htmz example</title>
  </head>
  <body>
    <div>
      <a href="page1.html#tab-panel" target="htmz">page1</a>
      <a href="page2.html#tab-panel" target="htmz">page2</a>
    </div>
    <div id="tab-panel">init</div>
    <script>
      function htmz(frame) {
        setTimeout(() =>
          document
            .querySelector(frame.contentWindow.location.hash || null)
            ?.replaceWith(...frame.contentDocument.body.children)
        );
      }
    </script>
    <iframe hidden name="htmz" onload="window.htmz(this)"></iframe>
  </body>
</html>
page1.html
<!DOCTYPE html>
<head><meta charset="utf-8"></head>
<div id="tab-panel">
  Page. 1
</div>
page2.html
<!DOCTYPE html>
<head><meta charset="utf-8"></head>
<div id="tab-panel">
  Page. 2
</div>

あとは作業ディレクトリでサーバーを起動すれば、

$ cd workspace/htmz-ex
$ ls -1
index.html
page1.html
page2.html
$ npm install -g vite
$ vite
  VITE v5.1.4  ready in 76 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

こんな感じになります(なぜかカーソルが消えちゃってわかりづらいですが)。

おわりに

タブによる切り替えや、文字列をクリックしたら入力フォームになるとか、簡単な動きであればhtmzで良いかもですね。

Discussion