実装パターン集
このガイドで紹介した設計方針を実装に落とし込む際の参考例を紹介します。
フレームワークや状況によって最適な実装は異なるため、あくまで「こういうアプローチもある」という参考として見てください。
インライン編集
Section titled “インライン編集”詳細画面で「編集モード」に切り替えるパターンです。
HTML + CSS のみ
Section titled “HTML + CSS のみ”<div class="inline-edit"> <input type="checkbox" id="edit-user-1" class="edit-toggle" />
<div class="view-mode"> <span>田中太郎</span> <label for="edit-user-1" class="btn">編集</label> </div>
<form class="edit-mode" method="post" action="/users/1"> <input type="text" name="name" value="田中太郎" /> <button type="submit">保存</button> <label for="edit-user-1" class="btn">キャンセル</label> </form></div>
<style>.edit-toggle { display: none; }.edit-mode { display: none; }.edit-toggle:checked ~ .view-mode { display: none; }.edit-toggle:checked ~ .edit-mode { display: block; }</style>Alpine.js での例
Section titled “Alpine.js での例”<div x-data="{ isEditing: false, draft: { name: '田中太郎' }, original: { name: '田中太郎' } }"> <form x-show="isEditing"> <input type="text" x-model="draft.name" /> <button type="button" x-on:click="$dispatch('save', draft); isEditing = false"> 保存 </button> <button type="button" x-on:click="draft = { ...original }; isEditing = false"> キャンセル </button> </form>
<div x-show="!isEditing"> <p x-text="draft.name"></p> <button x-on:click="original = { ...draft }; isEditing = true">編集</button> </div></div>- 編集画面への遷移が不要
- CSS のみ版はフォーム送信でサーバー処理
- Alpine.js 版はキャンセルで元の値に戻せる
インライン追加
Section titled “インライン追加”一覧画面で直接追加するパターンです。
HTML + CSS のみ
Section titled “HTML + CSS のみ”<table> <tbody> <tr><td>田中太郎</td></tr> <tr><td>佐藤花子</td></tr>
<tr class="inline-add"> <td> <input type="checkbox" id="add-user" class="add-toggle" />
<label for="add-user" class="add-button">+ 追加</label>
<form class="add-form" method="post" action="/users"> <input type="text" name="name" placeholder="名前を入力" /> <button type="submit">追加</button> <label for="add-user" class="btn">キャンセル</label> </form> </td> </tr> </tbody></table>
<style>.add-toggle { display: none; }.add-form { display: none; }.add-toggle:checked ~ .add-button { display: none; }.add-toggle:checked ~ .add-form { display: block; }</style>Alpine.js での例
Section titled “Alpine.js での例”<div x-data="{ users: [], isAdding: false, newName: '' }"> <table> <tbody> <template x-for="user in users" x-bind:key="user.id"> <tr> <td x-text="user.name"></td> </tr> </template>
<tr x-show="isAdding"> <td> <input type="text" x-model="newName" placeholder="名前を入力" /> <button x-on:click="users.push({ id: Date.now(), name: newName }); newName = ''; isAdding = false"> 追加 </button> <button x-on:click="isAdding = false">キャンセル</button> </td> </tr>
<tr x-show="!isAdding"> <td> <button x-on:click="isAdding = true">+ 追加</button> </td> </tr> </tbody> </table></div>- 登録画面への遷移が不要
- CSS のみ版はフォーム送信でサーバー処理
- Alpine.js 版は即座に一覧に反映される
取り消し(Undo)機能
Section titled “取り消し(Undo)機能”確認画面の代わりに「取り消し」で対応するパターンです。
Alpine.js での例
Section titled “Alpine.js での例”<div x-data="{ deleted: false, timeoutId: null }"> <div x-show="deleted"> 削除しました <button x-on:click="clearTimeout(timeoutId); deleted = false">取り消し</button> </div>
<button x-show="!deleted" x-on:click=" deleted = true; timeoutId = setTimeout(() => $dispatch('delete'), 5000) " > 削除 </button></div>- 確認画面より速い
- 「本当に削除しますか?」より「削除しました。取り消しますか?」
- 実際の削除は遅延実行する
確認モーダル
Section titled “確認モーダル”確認画面の代わりにモーダルを使うパターンです。
HTML + CSS のみ(JavaScript 不要)
Section titled “HTML + CSS のみ(JavaScript 不要)”<a href="#delete-confirm">削除</a>
<dialog id="delete-confirm"> <p>削除しますか?</p> <form method="post" action="/users/1"> <input type="hidden" name="_method" value="DELETE" /> <button type="submit">削除する</button> <a href="#">キャンセル</a> </form></dialog>
<style>dialog { display: none;}dialog:target { display: block;}</style>Alpine.js での例
Section titled “Alpine.js での例”<div x-data="{ isOpen: false }"> <button x-on:click="isOpen = true">削除</button>
<dialog x-bind:open="isOpen"> <p>田中太郎 を削除しますか?</p> <button x-on:click="$dispatch('delete'); isOpen = false">削除する</button> <button x-on:click="isOpen = false">キャンセル</button> </dialog></div>- 画面遷移なしで確認できる
- 背景が見えたまま操作できる
- セッション管理が不要
詳細の展開(CSS のみ)
Section titled “詳細の展開(CSS のみ)”詳細情報を展開するパターンです。JavaScript は不要です。
HTML + CSS のみ
Section titled “HTML + CSS のみ”<details> <summary>田中太郎</summary> <dl> <dt>メールアドレス</dt> <dd>tanaka@example.com</dd> <dt>部署</dt> <dd>営業部</dd> </dl></details>
<style>details { border: 1px solid #ccc; padding: 0.5em;}details[open] summary { font-weight: bold;}</style>- 詳細画面への遷移が不要
- 一覧で複数の詳細を同時に開ける
- JavaScript なしで動作する
状態変更の分離
Section titled “状態変更の分離”保存とは別に状態を変更するパターンです。
API 設計
Section titled “API 設計”PUT /items/:id → 内容を更新PATCH /items/:id/status → 状態のみ変更Alpine.js での例
Section titled “Alpine.js での例”<select x-data x-on:change="$dispatch('status-change', { status: $event.target.value })"> <option value="draft">下書き</option> <option value="published">公開</option> <option value="archived">アーカイブ</option></select>- 編集画面を開かなくても状態を変えられる
- 保存ボタンを押さなくても状態が変わる
- 一覧画面から直接操作できる
実装上の考慮点
Section titled “実装上の考慮点”インライン編集を実装する際に考慮すべきポイントです。
Optimistic UI(楽観的更新)
Section titled “Optimistic UI(楽観的更新)”ユーザーの操作を即座に画面に反映し、サーバー処理は裏で行います。
1. ユーザーが保存ボタンを押す2. 即座に UI を更新(成功を前提)3. 裏でサーバーに送信4. 失敗したら UI を元に戻す待ち時間がなくなり、体感速度が上がります。
エラー時のロールバック
Section titled “エラー時のロールバック”サーバー処理が失敗した場合の対応です。
- 変更前の値を保持しておく
- 失敗時は元の値に戻す
- ユーザーにエラーを通知する
- 再試行の手段を提供する
バリデーションのタイミング
Section titled “バリデーションのタイミング”| タイミング | 用途 |
|---|---|
| 入力中(リアルタイム) | 形式チェック(メール形式など) |
| フォーカス移動時 | 必須チェック |
| 保存時 | サーバー側の整合性チェック |
すべてを保存時にまとめると、ユーザーが戸惑うことがあります。
アクセシビリティ
Section titled “アクセシビリティ”- フォーカス管理:編集モードに入ったら入力欄にフォーカス
- キーボード操作:Enter で保存、Escape でキャンセル
- スクリーンリーダー:状態の変化を通知(
aria-live) - 十分なコントラスト:編集中であることを視覚的に明示
キーボードショートカットの例
Section titled “キーボードショートカットの例”// Enter で保存、Escape でキャンセルinput.addEventListener('keydown', (e) => { if (e.key === 'Enter') save(); if (e.key === 'Escape') cancel();});これらは一例であり、プロジェクトの状況に応じて調整が必要です。
共通して言えるのは:
- 画面遷移を減らすと、コードもシンプルになる傾向がある
- セッション管理が不要になると、状態の組み合わせが減る
- その場で操作できると、テストも書きやすくなる
- CSS だけで実現できるパターンは、JavaScript の複雑さを避けられる
設計がシンプルになれば、実装もシンプルになります。その逆も然りです。