ユーザがクリックするか、キーボードで Tab キーを使うとき、要素はフォーカスを受け取ります。また、ページが読み込まれたとき、デフォルトで要素にフォーカスを与える autofocus HTML属性もあり、フォーカスを取得するための別の手段です。
フォーカスは、たいてい “ここでデータを受け入れる準備をする” ことを意味します。つまり、コードを実行して初期化したり何かをロードすることができる瞬間です。
フォーカスを失う瞬間(“blur”)はより重要になります。それは、ユーザが他の場所をクリックしたり、Tab を押して次のフォームフィールドに移動したり、同様に他の手段もあります。
フォーカスを失うということは、通常 “データが入力された” ことを意味します。そのため、それをチェックしたり、サーバに保存したりするコードを実行することができます。
フォーカスイベントを扱う際には重要な特性があります。ここではそれを説明するためにベストを尽くします。
イベント focus/blur
focus イベントはフォーカス時に呼ばれ、blur は要素がフォーカスを失ったときに呼ばれます。
インプットフィールドのバリデーションでそれらを使ってみましょう。
以下の例では:
blurハンドラは email が入力されたかどうかをチェックし、もしそうでなければエラーを表示します。focusハンドラはエラーメッセージを隠します(blur時にサイドチェックされます)。:
<style>
.invalid { border-color: red; }
#error { color: red }
</style>
Your email please: <input type="email" id="input">
<div id="error"></div>
<script>
input.onblur = function() {
if (!input.value.includes('@')) { // not email
input.classList.add('invalid');
error.innerHTML = 'Please enter a correct email.'
}
};
input.onfocus = function() {
if (this.classList.contains('invalid')) {
// ユーザは何かを再入力したいので "エラー" 表示を削除します
this.classList.remove('invalid');
error.innerHTML = "";
}
};
</script>
現代の HTML では input の属性を使って多くのバリデーションを行うことができます: required, pattern などです。そして、時にはそれらが丁度私たちが必要なことである場合もあります。JavaScriptは、より柔軟性が必要な場合に使用できます。 その値が正しい場合、変更された値をサーバ上に自動的に送信することもできます。
メソッド focus/blur
メソッド elem.focus() と elem.blur() は要素のフォーカスを設定/解除します。
例えば、値が有効でない場合には訪問者が input から離れられないようにしてみましょう。:
<style>
.error {
background: red;
}
</style>
Your email please: <input type="email" id="input">
<input type="text" style="width:220px" placeholder="make email invalid and try to focus here">
<script>
input.onblur = function() {
if (!this.value.includes('@')) { // not email
// エラーを表示
this.classList.add("error");
// ...その後、フォーカスを戻します
input.focus();
} else {
this.classList.remove("error");
}
};
</script>
これは、Firefox (bug) を除いたすべてのブラウザで動作します。
入力域に何かを入力した後、Tab や クリックで <input> から離れようとすると、onblur はフォーカスを戻します。
onblur の中で event.preventDefault() を呼び出すことで “フォーカスを失うことを防ぐ” ことはできない点に注意してください。なえなら、onblur は要素がフォーカスを失った 後 に動作するためです。
フォーカス解除は多くの理由により起こります。
その1つは、訪問者がどこか他の場所をクリックした時です。しかし JavaScript 自体もそれを引き起こす可能性があります。例えば:
alertはフォーカスを自身へ移動させるため、フォーカス解除(blur)が要素で起きます。またalertが消えたとき、フォーカスが戻ります(focusイベント)。- もしも要素が DOM から削除された場合も、フォーカス解除が起こります。後で再挿入したとしても、フォーカスは戻りません。
これらの特徴により、focus/blur ハンドラが必要ないときにトリガすることがあります。
最善の策は、それらのイベントを使うとき注意を払うことです。ユーザが開始したフォーカス解除を追跡したい場合は、自分自身でフォーカス解除を回避する必要があります。
任意の要素にフォーカスを当てる: tabindex
デフォルトでは、多くの要素はフォーカスをサポートしていません。
この一覧はブラウザによって異なりますが、正しいことが1つあります: focus/blur のサポートは、<button>, <input>, <select>, <a> など、訪問者が対話できる要素に対して保証されています。
一方、<div>, <span>, <table> のような体裁のために存在する要素は、デフォルトではフォーカス不可です。メソッド elem.focus() はそれらに対しては機能せず、focus/blur イベントがトリガされることはありません。
HTML 属性 tabindex を使用することでこれを変更することが可能です。
この属性の目的は、Tab を使って要素間を切り替える際に、要素の順番を指定することです。
つまり: 2つの要素があり、1つ目は tabindex="1" 、2つ目は tabindex="2" を持っている場合、最初の要素で Tab を押すと2つ目に移動します。
2つの特別な値があります:
tabindex="0"は要素を最後にします。tabindex="-1"は Tab がその要素を無視することを意味します。
どの要素も tabindex を持っていればフォーカスをサポートします。
例えばここにリストがあります。最初の項目をクリックして Tab を押してください:
最初の項目をクリックしてタブを押してください。指定順に移動します。多くの後続のタブはフォーカスを例の iframe から移動できることに留意してください。
<ul>
<li tabindex="1">One</li>
<li tabindex="0">Zero</li>
<li tabindex="2">Two</li>
<li tabindex="-1">Minus one</li>
</ul>
<style>
li { cursor: pointer; }
:focus { outline: 1px dashed green; }
</style>
順序は 1 - 2 - 0 (ゼロは常に最後) です。通常、<li> はフォーカスをサポートしませんが、tabindex はイベントや :focus のスタイリングと合わせてフォーカスを有効にします。
elem.tabIndex も機能しますelem.tabIndex プロパティを使用することで、JavaScript から tabindex を追加することができます。これは同じ効果があります。
移譲: focuin/focusout
イベント focus と blur はバブルしません。
例えば、次のように onfocus を <form> に置いて強調表示させる、ということはできません:
<!-- on focusing in the form -- add the class -->
<form onfocus="this.className='focused'">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
上の例は機能しません。なぜなら、ユーザが <input> にフォーカスしたとき、focus イベントはその input でのみトリガされるためです。バブルしないので、form.onfocus がトリガされることはありません。
ここには2つの解決策があります。
1つ目は、面白い歴史的な機能です: focus/blur はバブルしませんが、キャプチャリングフェーズで伝搬します。
これは機能します:
<form id="form">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
// キャプチャリングフェーズにハンドラを設定します(最後の引数を true)
form.addEventListener("focus", () => form.classList.add('focused'), true);
form.addEventListener("blur", () => form.classList.remove('focused'), true);
</script>
2つ目は、focusin と focusout イベントです – focus/blur とまったく同じですが、バブルします。
それらは on<event> ではなく、elem.addEventListener で割り当てなければならないことに注意してください。
従って、ここに別の動作するバリエーションがあります。:
<form id="form">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
form.addEventListener("focusin", () => form.classList.add('focused'));
form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>
サマリ
イベント focus と blur は要素にフォーカスが当たる/外れるときにトリガされます。
それらの特別なこと:
- それらはバブルしません。代わりにキャプチャリングフェーズや
focusin/focusoutを使うことができます。 - ほとんどの要素はデフォルトではフォーカスをサポートしません。何かをフォーカス可能にするには
tabindexを使います。
現在フォーカスされている要素は document.activeElement として利用できます。
コメント
<code>タグを使ってください。複数行の場合は<pre>を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。