要素は、ユーザーがクリックするか、キーボードのTabキーを使用するとフォーカスを受け取ります。また、ページが読み込まれたときにデフォルトで要素にフォーカスを当てる autofocus HTML属性や、フォーカスを取得する他の手段もあります。
一般的に、要素にフォーカスが当たるということは、「ここにデータを受け入れる準備をする」という意味で、必要な機能を初期化するためのコードを実行できる瞬間です。
フォーカスを失う(「ブラー」)瞬間はさらに重要となる場合があります。ユーザーが別の場所をクリックしたり、Tabを押して次のフォームフィールドに移動したり、他の手段がある場合です。
一般的に、フォーカスを失うということは、「データが入力された」という意味なので、それをチェックしたり、サーバーに保存したりするなどのコードを実行できます。
フォーカスイベントを扱う際には重要な特性があります。それらについては後で詳しく説明します。
イベント focus/blur
フォーカス時にfocusイベントが呼び出され、要素がフォーカスを失うとblurイベントが呼び出されます。
入力フィールドの検証に使用してみましょう。
以下の例では、
blurハンドラーは、フィールドにメールアドレスが入力されているかどうかをチェックし、入力されていない場合はエラーを表示します。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')) {
// remove the "error" indication, because the user wants to re-enter something
this.classList.remove('invalid');
error.innerHTML = "";
}
};
</script>
最新のHTMLでは、required、patternなどの入力属性を使用して多くの検証を実行できます。そして、必要なものが得られることもあります。JavaScriptは、より柔軟性が必要な場合に使用できます。また、変更された値が正しい場合は、自動的にサーバーに送信することもできます。
メソッド focus/blur
メソッドelem.focus()とelem.blur()は、要素のフォーカスを設定/解除します。
たとえば、値が無効な場合、訪問者が入力を離れることができないようにしてみましょう。
<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
// show the error
this.classList.add("error");
// ...and put the focus back
input.focus();
} else {
this.classList.remove("error");
}
};
</script>
これはFirefox以外のすべてのブラウザで動作します(バグ)。
入力に何かを入力し、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を使用して変更できます。
tabindexを持つ要素は、フォーカス可能になります。属性の値は、Tab(または同様のもの)を使用して要素間を切り替えるときの要素の順序番号です。
つまり、2つの要素があり、最初の要素にtabindex="1"、2番目の要素にtabindex="2"がある場合、最初の要素でTabを押すと、フォーカスが2番目の要素に移動します。
切り替え順序は次のとおりです。tabindexが1以上の要素が最初に表示され(tabindex順)、次にtabindexがない要素(例: 通常の<input>)が表示されます。
一致するtabindexがない要素は、ドキュメントソースの順序(デフォルトの順序)で切り替えられます。
2つの特別な値があります。
-
tabindex="0"は、tabindexがない要素の中に要素を配置します。つまり、要素を切り替えるとき、tabindex=0の要素はtabindex ≥ 1の要素の後に移動します。通常、これは要素をフォーカス可能にし、デフォルトの切り替え順序を維持するために使用されます。
<input>と同等のフォームの一部になるように要素を作成します。 -
tabindex="-1"は、要素に対するプログラムによるフォーカスのみを許可します。Tabキーはこのような要素を無視しますが、メソッドelem.focus()は機能します。
たとえば、ここにリストがあります。最初の項目をクリックして、Tabキーを押します。
Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example.
<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を追加できます。これは同じ効果があります。
委譲: focusin/focusout
イベントfocusとblurはバブリングしません。
たとえば、<form>にonfocusを配置して、次のように強調表示することはできません。
<!-- 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イベントはその入力でのみトリガーされるためです。これはバブリングされません。したがって、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>
// put the handler on capturing phase (last argument 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…)を使用してください。