JavaScriptの罠:alert()後のfocus()が動かない問題と解決法
setTimeout()を使った確実な対処方法
よくある困った状況
入力チェックでエラーを見つけた時、alert()を出して、その後に入力欄にfocus()で戻したい…でも動かない!
動かないコード
BAD
function validateInput() {
const input = document.getElementById('username');
if (!input.value.trim()) {
alert('ユーザー名を入力してください');
input.focus(); // ← 動かない!
}
}
なぜ動かないのか
原因:フォーカス処理の競合
- alert()を閉じる動作が非同期で実行される
- alert()のウィンドウからフォーカスを外す処理が走る
- input.focus()の処理も同時に走る
- 結果:フォーカスが当たった瞬間に外される
ブラウザごとに挙動が違う
- Firefox: フォーカスが移動しない程度
- Chrome: 無限にalert()が発動し続ける
解決策:setTimeout()で非同期化
ポイント:フォーカス処理を1ミリ秒ずらす
alert()を閉じる処理が完全に終わった後にfocus()を実行すれば、競合しません。
GOOD
function validateInput() {
const input = document.getElementById('username');
if (!input.value.trim()) {
alert('ユーザー名を入力してください');
// setTimeout で非同期化!
setTimeout(function() {
input.focus(); // フォーカスを戻す
input.select(); // 全選択
}, 1); // 1ミリ秒の遅延
}
}
モダンな書き方(アロー関数)
function validateInput() {
const input = document.getElementById('username');
if (!input.value.trim()) {
alert('ユーザー名を入力してください');
// アロー関数でスッキリ
setTimeout(() => {
input.focus();
input.select();
}, 1);
}
}
実践例:複数の入力欄がある場合
function validateForm() {
const username = document.getElementById('username');
const email = document.getElementById('email');
// ユーザー名チェック
if (!username.value.trim()) {
alert('ユーザー名を入力してください');
setTimeout(() => {
username.focus();
username.select();
}, 1);
return false;
}
// メールアドレスチェック
if (!email.value.trim()) {
alert('メールアドレスを入力してください');
setTimeout(() => {
email.focus();
email.select();
}, 1);
return false;
}
return true;
}
おまけ:alert()を使わない方法
モダンなWebアプリでは、alert()よりもユーザーフレンドリーな方法があります:
- HTML5のrequired属性を使う(最もシンプル)
- インラインエラーメッセージを表示
- トースト通知やモーダルダイアログを使う
// HTML5のrequired属性を使う例
<input type="text" id="username" required>
// インラインエラーメッセージの例
function validateInput() {
const input = document.getElementById('username');
const errorMsg = document.getElementById('error');
if (!input.value.trim()) {
errorMsg.textContent = 'ユーザー名を入力してください';
errorMsg.style.display = 'block';
input.focus(); // alert()がないので普通に動く!
return false;
}
}
まとめ
- ・ alert()の後にfocus()を使う場合は、必ずsetTimeout()で1ミリ秒遅延させる
- ・ ブラウザによって挙動が異なるため、この対策は必須
- ・ 可能であれば、alert()よりもモダンなUI手法を検討する
