要素は、ユーザーがクリックするか、キーボードの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…)を使用してください。