はじめに
Webアプリケーションでよく見かける「追加」ボタンを押して新しいフォームを作成する機能。
ユーザーは追加後、すぐに入力を始めたいはずです。しかし、マウスやTabキーで入力欄を選択するのは手間がかかります。
この記事では、フォームを追加したら自動的に入力欄にフォーカスが移動する機能の実装方法を紹介します。
こんな課題を解決します
- フォームを追加したら、マウス操作なしですぐに入力を始めたい
- 画面が自動でスクロールして、追加したフォームが見えるようにしたい
実装のステップ
1. 基本的な準備
まずはフォーム要素を管理するための基本的な実装から始めます
const FormListExample = () => {
// フォームのリストを管理(各フォームにユニークなIDを付与)
const [forms, setForms] = useState([{ id: crypto.randomUUID() }]);
// DOM要素への参照を保持
const formsRef = useRef({});
// 要素数の変化を検知するための参照
const formsLengthRef = useRef(forms.length);
// ...他の実装
};
2. 要素への参照を管理
フォーカスを制御するには、DOM要素への参照が必要です
// 要素への参照を管理するための関数
const setFormRef = useCallback((id, node) => {
if (node) {
formsRef.current[id] = node;
} else {
delete formsRef.current[id];
}
}, []);
3. フォーム追加時のフォーカス制御
フォームが追加されたときの処理を実装します
useEffect(() => {
// 初回レンダリング時は処理をスキップ
if (formsLengthRef.current === 0) {
formsLengthRef.current = forms.length;
return;
}
// フォームが追加された場合
if (forms.length > formsLengthRef.current) {
const lastId = forms[forms.length - 1].id;
const lastElement = formsRef.current[lastId];
if (lastElement) {
// 追加されたフォームが見えるようにスクロール
lastElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
// 最初の入力欄にフォーカスを設定
const input = lastElement.querySelector('input, select, textarea');
if (input instanceof HTMLElement) {
input.focus();
}
}
}
// 現在の要素数を更新
formsLengthRef.current = forms.length;
}, [forms]);
実装のポイント解説
1. なぜ参照(ref)を使うのか
- DOM要素への直接アクセスが必要
- 状態更新による再レンダリングに影響されない
- 要素の追加に柔軟に対応できる
2. フォーカス制御のコツ
-
querySelector
で適切な入力要素を選択 - 複数の入力欄がある場合は、最初の要素を選択
- スクロール後にフォーカスを設定することで、視認性を確保
3. スクロール制御のポイント
-
scrollIntoView
のオプション設定が重要-
behavior: 'smooth'
でスムーズなスクロール -
block: 'center'
で要素を中央に表示
-
まとめ
とても使いやすくなったと思います。
些細な部分ではありますが、このような部分もこだわった機能を実装していくことがよりユーザーファーストなサービスを作成する意識が芽生えると思っています。
引き続き頑張ります!