htmx 逆引きレシピ
依存セレクトを作るには?

公開日:
最終更新日:

依存セレクトは、「上の<select>の選択」に応じて「下の<select>の候補」を切り替えるUIです。
htmxを使えばJavaScriptなしでも、必要な部分だけをサーバーから取得して差し替えるだけで、実務で使える依存セレクトをシンプルに実装できます。

このページでは「部署→メンバー」「カテゴリ→サブカテゴリ「都道府県→市区町村」について、hx-get / hx-trigger / hx-target / hx-swap の基本だけで作る手順をデモ付きで解説します。

使用するhtmx属性

  • hx-get:親<select>の変更に合わせて、GETで子<select>のHTMLを取得する
  • hx-trigger:リクエストを発火するタイミングを指定(例:change
  • hx-target:差し替える先の要素(CSSセレクタ)を指定(例:子<select>の#DEMO_CITYなど)
  • hx-swap:差し替え方を指定(このレシピでは outerHTML で子<select>自体を丸ごと入れ替える)

利用シーン

  • 「都道府県→市区町村」:上位の選択に応じて、下位の候補だけを切り替えたい
  • 「部署→メンバー」:部署を選んだら、その部署のメンバーだけを選べるようにしたい
  • 「カテゴリ→サブカテゴリ」:カテゴリ変更に合わせて、サブカテゴリの選択肢を動的に更新したい

① 依存セレクト(部署 → メンバー)

部署を選ぶと、その部署に所属するメンバーだけが候補に出る「依存セレクト」の例です。
JavaScriptなしで、下位の<select>だけを差し替えるので、フォームをシンプルに保てます。

HTML

<div class="DEMO">

	<h4>① 依存セレクト(部署 → メンバー)</h4>

	<form id="DEMO_DEP_DEPT_FORM" class="FORM">

		<label>
			部署
			<select
				id="DEMO_DEPT"
				name="dept"

				hx-get="/htmx/demo/_dependent_select_1.php"
				hx-trigger="change"
				hx-target="#DEMO_MEMBER"
				hx-swap="outerHTML"
			>
				<option value="">選択してください</option>
				<option value="dev">開発</option>
				<option value="sales">営業</option>
				<option value="hr">人事</option>
			</select>
		</label>

		<label>
			メンバー
			<select id="DEMO_MEMBER" name="member" disabled>
				<option value="">部署を選択してください</option>
			</select>
		</label>

	</form>

</div>

PHP

<?php
// 型を厳密に扱う(意図しない型変換を防ぐ)
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ用(XSS対策)
function h(string $s): string {
	// 特殊文字を安全な文字列に変換する
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// 部署→メンバー(デモ用データ。実務ではDB/APIに置き換える)
$membersMap = [

	// 開発
	'dev' => [
		'tanaka' => '田中',
		'sato'   => '佐藤',
		'suzuki' => '鈴木',
	],

	// 営業
	'sales' => [
		'kato'   => '加藤',
		'kobayashi' => '小林',
		'yamada' => '山田',
	],

	// 人事
	'hr' => [
		'ito'    => '伊藤',
		'nakamura' => '中村',
		'kimura' => '木村',
	],
];

// GETパラメータ dept(部署)を受け取る(なければ空)
$dept = (string)($_GET['dept'] ?? '');

// 前後空白を除去する
$dept = trim($dept);

// 返す<select>の開始タグ(有効状態:disabledなし)
$selectOpen = '<select id="DEMO_MEMBER" name="member">';

// 部署が未選択、または未知の値なら「未選択状態」を返す
if ($dept === '' || !array_key_exists($dept, $membersMap)) {

	// disabled付きの<select>を出力する(初期状態)
	echo '<select id="DEMO_MEMBER" name="member" disabled>';

	// 案内オプションを出力する
	echo '<option value="">部署を選択してください</option>';

	// selectを閉じる
	echo '</select>';

	// ここで処理を終了する
	exit;
}

// 該当部署のメンバーリスト(配列)を取り出す
$members = $membersMap[$dept];

// 有効な<select>(disabledなし)を出力開始
echo $selectOpen;

// 先頭の案内オプションを出力する
echo '<option value="">メンバーを選択してください</option>';

// メンバーの配列を1件ずつ<option>にする
foreach ($members as $value => $label) {

	// value(送信値)を安全にエスケープする
	$v = h((string)$value);

	// label(表示名)を安全にエスケープする
	$l = h((string)$label);

	// optionタグを出力する
	echo "<option value=\"{$v}\">{$l}</option>";
}

// selectを閉じる
echo '</select>';

デモ

① 依存セレクト(部署 → メンバー)

解説

  • 部署<select>にhx-getを付け、選択が変わるたびにサーバーへGETリクエストを送ります。
  • hx-trigger="change"で、部署の変更タイミングでだけ発火します。
  • hx-target="#DEMO_MEMBER"で、差し替え対象を「メンバー<select>」に限定します。
  • hx-swap="outerHTML"で、メンバー<select>自体を丸ごと入れ替えます(disabled解除も同時にできる)。
  • サーバー(PHP)はGETパラメータdeptを受け取り、該当部署のメンバー一覧を<option>として組み立てます。
  • 部署が未選択/不正な値なら、disabled付きのメンバー<select>を返して「選んでください」状態に戻します。

② 依存セレクト(カテゴリ → サブカテゴリ)

カテゴリを選ぶと、そのカテゴリに対応するサブカテゴリだけが候補に出る「依存セレクト」の例です。
下位の<select>だけを更新するので、画面全体を再読み込みせずに軽快に切り替えられます。

HTML

<div class="DEMO">

	<h4>② 依存セレクト(カテゴリ → サブカテゴリ)</h4>

	<form id="DEMO_DEP_CATEGORY_FORM" class="FORM">

		<label>
			カテゴリ
			<select
				id="DEMO_CATEGORY"
				name="category"

				hx-get="/htmx/demo/_dependent_select_2.php"
				hx-trigger="change"
				hx-target="#DEMO_SUBCATEGORY"
				hx-swap="outerHTML"
			>
				<option value="">選択してください</option>
				<option value="it">IT</option>
				<option value="office">オフィス用品</option>
				<option value="food">食品</option>
			</select>
		</label>

		<label>
			サブカテゴリ
			<select id="DEMO_SUBCATEGORY" name="subcategory" disabled>
				<option value="">カテゴリを選択してください</option>
			</select>
		</label>

	</form>

</div>

PHP

<?php
// 型を厳密に扱う(意図しない型変換を防ぐ)
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ用(XSS対策)
function h(string $s): string {
	// 特殊文字を安全な文字列に変換する
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// カテゴリ→サブカテゴリ(デモ用データ。実務ではDB/APIに置き換える)
$subMap = [

	// IT
	'it' => [
		'laptop'   => 'ノートPC',
		'monitor'  => 'モニター',
		'network'  => 'ネットワーク機器',
	],

	// オフィス用品
	'office' => [
		'stationery' => '文房具',
		'chair'      => '椅子・デスク',
		'storage'    => '収納',
	],

	// 食品
	'food' => [
		'drink'   => '飲料',
		'snack'   => 'お菓子',
		'fruit'   => '果物',
	],
];

// GETパラメータ category(カテゴリ)を受け取る(なければ空)
$category = (string)($_GET['category'] ?? '');

// 前後空白を除去する
$category = trim($category);

// 返す<select>の開始タグ(有効状態:disabledなし)
$selectOpen = '<select id="DEMO_SUBCATEGORY" name="subcategory">';

// カテゴリが未選択、または未知の値なら「未選択状態」を返す
if ($category === '' || !array_key_exists($category, $subMap)) {

	// disabled付きの<select>を出力する(初期状態)
	echo '<select id="DEMO_SUBCATEGORY" name="subcategory" disabled>';

	// 案内オプションを出力する
	echo '<option value="">カテゴリを選択してください</option>';

	// selectを閉じる
	echo '</select>';

	// ここで処理を終了する
	exit;
}

// 該当カテゴリのサブカテゴリリスト(配列)を取り出す
$subs = $subMap[$category];

// 有効な<select>(disabledなし)を出力開始
echo $selectOpen;

// 先頭の案内オプションを出力する
echo '<option value="">サブカテゴリを選択してください</option>';

// サブカテゴリの配列を1件ずつ<option>にする
foreach ($subs as $value => $label) {

	// value(送信値)を安全にエスケープする
	$v = h((string)$value);

	// label(表示名)を安全にエスケープする
	$l = h((string)$label);

	// optionタグを出力する
	echo "<option value=\"{$v}\">{$l}</option>";
}

// selectを閉じる
echo '</select>';

デモ

② 依存セレクト(カテゴリ → サブカテゴリ)

解説

  • カテゴリ<select>にhx-getを付け、選択が変わるたびにサーバーへGETリクエストを送ります。
  • hx-trigger="change"で、カテゴリの変更タイミングでだけ発火します。
  • hx-target="#DEMO_SUBCATEGORY"で、差し替え対象を「サブカテゴリ<select>」に限定します。
  • hx-swap="outerHTML"で、サブカテゴリ<select>自体を丸ごと入れ替えます(disabled解除も同時にできる)。
  • サーバー(PHP)はGETパラメータcategoryを受け取り、該当カテゴリのサブカテゴリ一覧を<option>として組み立てます。
  • カテゴリが未選択/不正な値なら、disabled付きのサブカテゴリ<select>を返して「選んでください」状態に戻します。

③ 依存セレクト(都道府県 → 市区町村)

都道府県を選ぶと、その都道府県に属する市区町村だけが候補に出る「依存セレクト」の例です。
JavaScriptなしで、下位の<select>だけを差し替えるので、フォームをシンプルに保てます。

HTML

<div class="DEMO">

	<h4>③ 依存セレクト(都道府県 → 市区町村)</h4>

	<form id="DEMO_DEP_SELECT_FORM" class="FORM">

		<label>
			都道府県
			<select
				id="DEMO_PREF"
				name="pref"

				hx-get="/htmx/demo/_dependent_select_3.php"
				hx-trigger="change"
				hx-target="#DEMO_CITY"
				hx-swap="outerHTML"
			>
				<option value="">選択してください</option>
				<option value="13">東京都</option>
				<option value="27">大阪府</option>
				<option value="23">愛知県</option>
			</select>
		</label>

		<label>
			市区町村
			<!-- 初期状態は disabled。都道府県を選んだら、この<select>自体を差し替える -->
			<select id="DEMO_CITY" name="city" disabled>
				<option value="">都道府県を選択してください</option>
			</select>
		</label>

	</form>

	<p class="HTMX-NOTE">
		都道府県を変更すると、市区町村の<select>だけが更新されます(ページ遷移なし)。
	</p>

</div>

PHP

<?php
// 型を厳密に扱う(意図しない型変換を防ぐ)
declare(strict_types=1);

// 返すのはHTML(UTF-8)
header('Content-Type: text/html; charset=UTF-8');

// HTMLエスケープ用(XSS対策)
function h(string $s): string {
	// 特殊文字を安全な文字列に変換する
	return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

// 都道府県→市区町村(デモ用データ。実務ではDB/APIに置き換える)
$citiesMap = [

	// 13: 東京都
	'13' => [
		'chiyoda'  => '千代田区',
		'chuo'     => '中央区',
		'shinjuku' => '新宿区',
		'shibuya'  => '渋谷区',
	],

	// 27: 大阪府
	'27' => [
		'osaka'    => '大阪市',
		'sakai'    => '堺市',
		'higashi'  => '東大阪市',
		'toyono'   => '豊能町',
	],

	// 23: 愛知県
	'23' => [
		'nagoya'     => '名古屋市',
		'okazaki'    => '岡崎市',
		'ichinomiya' => '一宮市',
		'tahara'     => '田原市',
	],

];

// GETパラメータ pref(都道府県コード)を受け取る(なければ空)
$pref = (string)($_GET['pref'] ?? '');

// 前後空白を除去する
$pref = trim($pref);

// 返す<select>の開始タグ(有効状態:disabledなし)
$selectOpen = '<select id="DEMO_CITY" name="city">';

// 都道府県が未選択、または未知の値なら「未選択状態」を返す
if ($pref === '' || !array_key_exists($pref, $citiesMap)) {

	// disabled付きの<select>を出力する(初期状態)
	echo '<select id="DEMO_CITY" name="city" disabled>';

	// 案内オプションを出力する
	echo '<option value="">都道府県を選択してください</option>';

	// selectを閉じる
	echo '</select>';

	// ここで処理を終了する
	exit;
}

// 該当都道府県の市区町村リスト(配列)を取り出す
$cities = $citiesMap[$pref];

// 有効な<select>(disabledなし)を出力開始
echo $selectOpen;

// 先頭の案内オプションを出力する
echo '<option value="">市区町村を選択してください</option>';

// 市区町村の配列を1件ずつ<option>にする
foreach ($cities as $value => $label) {

	// value(送信値)を安全にエスケープする
	$v = h((string)$value);

	// label(表示名)を安全にエスケープする
	$l = h((string)$label);

	// optionタグを出力する
	echo "<option value=\"{$v}\">{$l}</option>";
}

// selectを閉じる
echo '</select>';

デモ

③ 依存セレクト(都道府県 → 市区町村)

都道府県を変更すると、市区町村の<select>だけが更新されます(ページ遷移なし)。

解説

  • 都道府県<select>にhx-getを付け、選択が変わるたびにサーバーへGETリクエストを送ります。
  • hx-trigger="change"で、都道府県の変更タイミングでだけ発火します。
  • hx-target="#DEMO_CITY"で、差し替え対象を「市区町村<select>」に限定します。
  • hx-swap="outerHTML"で、市区町村<select>自体を丸ごと入れ替えます(disabled解除も同時にできる)。
  • サーバー(PHP)はGETパラメータprefを受け取り、該当都道府県の市区町村一覧を<option>として組み立てます。
  • 都道府県が未選択/不正な値なら、disabled付きの市区町村<select>を返して「選んでください」状態に戻します。

このページの著者

もちもちみかん(システムエンジニア)

社内SEとしてグループ企業向けの業務アプリを要件定義〜運用まで一気通貫で担当しています。

経験:Webアプリ/業務システム

得意:PHP・JavaScript・MySQL・CSS

個人実績:フォーム生成基盤クイズ学習プラットフォーム

詳しいプロフィールはこちら!  もちもちみかんのプロフィール

もちもちみかん0系くん
TOPへ

もちもちみかん.comとは


このサイトでは、コーディングがめんどうくさい人向けのお助けツールとして、フォームやCSSをノーコードで生成できる、
 もちもちみかん.forms
 もちもちみかん.css1
 もちもちみかん.css2
と言ったジェネレーターを用意してます。

また、このサイトを通じて、「もちもちみかん」のかわいさを普及したいとかんがえてます!