検索ページを開いたら、すぐキーワードを打ち込めた。ログイン画面を開いたら、カーソルがメールアドレス欄で点滅していた。こうした「開いた瞬間に入力できる」状態は、HTML の autofocus 属性を1つ付けるだけで実現できます。この記事では autofocus の基本的な書き方から、JavaScript の focus() との使い分け、そして使う前に必ず知っておきたいアクセシビリティ上の注意点までを解説します。
目次
autofocus 属性で何ができるのか
autofocus は、ページが読み込まれたときに、その要素へ自動でフォーカスを当てるための属性です。フォーカスとは、キーボード入力を受け取る対象になっている状態のこと。テキスト入力欄であればカーソルが点滅し、そのままキーを打てば文字が入ります。
ユーザーがマウスで入力欄をクリックする、あるいは Tab キーを何度も押して目的の欄までたどり着く、という手間を省けます。検索専用ページの検索窓、ログインフォームのメールアドレス欄、モーダルで開く問い合わせフォームの最初の項目といった、そのページに来た人が十中八九そこへ入力する場面で効果を発揮します。
基本の使い方
フォーカスを当てたい要素に autofocus と書くだけです。値は必要ありません。
<form action="/search" method="get">
<label for="q">サイト内を検索</label>
<input type="search" id="q" name="q" autofocus>
<button type="submit">検索</button>
</form>
このページをブラウザーで開くと、<input> にカーソルが入った状態で表示されます。JavaScript は一切必要ありません。
真偽値属性なので値は書かない
autofocus は disabled や required と同じ真偽値属性(boolean attribute)です。真偽値属性は「書いてあれば有効、書いていなければ無効」という二択で、値そのものは一切見られません。そのため autofocus="true" のように値を書く必要はありません。
ここで注意したいのが、autofocus="false" と書いても無効にはならないという点です。属性が存在している時点で有効と判断されるため、「false だからオフになる」という直感は通用しません。条件によってフォーカスを当てたり当てなかったりしたい場合は、値を切り替えるのではなく属性そのものを出力しないようにしてください。
<!-- どれも「有効」。すべて同じ意味になる -->
<input autofocus>
<input autofocus="">
<input autofocus="autofocus">
<input autofocus="true">
<input autofocus="false"> <!-- これも有効になってしまう -->
<!-- 無効にしたいなら、属性を書かない -->
<input>
なお、XHTML など属性値を省略できない書き方に合わせる場合は autofocus="autofocus" と書く習慣もありますが、通常の HTML では autofocus だけで問題ありません。
動かして確かめる
実際の挙動を見てみましょう。検索フォームの入力欄に autofocus を付け、フォーカス中だと分かるように :focus で枠線の色を変えています。プレビューが表示されると、入力欄が青く縁取られてカーソルが入った状態になります(環境によっては、プレビュー内を一度クリックしてから確認してください)。
<form class="search" action="/search" method="get">
<label for="q">サイト内を検索</label>
<div class="search__row">
<!-- ページを開いた時点で、この入力欄にフォーカスが当たる -->
<input type="search" id="q" name="q" placeholder="キーワードを入力" autofocus>
<button type="submit">検索</button>
</div>
</form>
.search {
font-family: sans-serif;
padding: 24px;
}
.search label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.search__row {
display: flex;
gap: 8px;
}
.search input {
flex: 1;
padding: 10px 12px;
border: 2px solid #ccc;
border-radius: 6px;
font-size: 16px;
}
/* フォーカスが当たっている状態を分かりやすくする */
.search input:focus {
border-color: #007bff;
outline: 3px solid rgba(0, 123, 255, 0.3);
outline-offset: 1px;
}
.search button {
padding: 10px 20px;
border: none;
border-radius: 6px;
background: #007bff;
color: #fff;
font-size: 16px;
cursor: pointer;
}
autofocus を外すと、フォーカスはどこにも当たっていない状態でページが表示されます。Tab キーを1回押して初めて入力欄に入る、という違いを比べてみてください。
input 以外の要素にも指定できる
autofocus はフォーム部品専用の属性だと思われがちですが、実際にはグローバル属性です。つまり、フォーカスを受け取れる要素であれば何にでも指定できます。
<!-- 複数行のテキスト入力 -->
<textarea name="body" autofocus></textarea>
<!-- セレクトボックス -->
<select name="pref" autofocus>
<option>東京都</option>
</select>
<!-- ボタン -->
<button type="button" autofocus>次へ進む</button>
<!-- 編集可能な領域 -->
<div contenteditable autofocus>ここを編集できます</div>
<!-- tabindex でフォーカス可能にした要素 -->
<div tabindex="-1" autofocus>お知らせ本文</div>
逆に言えば、そもそもフォーカスできない要素に付けても何も起こりません。<p> や <div> は既定ではフォーカスを受け取れないので、tabindex を付けてフォーカス可能にしたうえで autofocus を指定する必要があります。読み上げの起点を移したいだけで Tab キーの順序には入れたくない場合は、tabindex="-1" が適しています。
dialog を開いたときの初期フォーカス
<dialog> 要素を showModal() で開くと、ブラウザーはダイアログ内のどこかにフォーカスを移します。このとき、ダイアログの中に autofocus を付けた要素があれば、そこが初期フォーカスの対象になります。
<dialog id="edit-dialog">
<form method="dialog">
<label for="title">タイトル</label>
<!-- 開いた瞬間、この欄から入力を始められる -->
<input type="text" id="title" autofocus>
<button>保存</button>
</form>
</dialog>
autofocus を書かなかった場合、どこにフォーカスが当たるかはブラウザーの判断に委ねられます。「閉じる」ボタンが先頭にあるダイアログでは、開いた直後に閉じるボタンへフォーカスが当たってしまい、意図と違う挙動に見えることもあります。ダイアログの初期フォーカスをこちらで決めたいなら、autofocus を明示すると考えておくとよいでしょう。
なお autofocus がダイアログで働くのは、ページ読み込み時ではなく showModal() や show() でダイアログが開かれたタイミングです。閉じているダイアログの中の要素は表示されていないため、ページを開いただけではフォーカスされません。
1つの文書に書けるのは1つだけ
フォーカスは同時に1か所にしか当たりません。そのため autofocus も、1つの文書につき1つが原則です。
仕様では、複数の要素に autofocus が付いている場合、文書内で最初に現れたものが採用され、2つ目以降は無視されると定められています。エラーにはならず、静かに無視されるだけなので、気づきにくいのが厄介なところです。
<input type="text" name="name" autofocus> <!-- こちらが有効 -->
<input type="email" name="email" autofocus> <!-- 無視される -->
問題が起きやすいのは、部品化されたテンプレートを組み合わせるときです。共通のフォーム部品に autofocus を埋め込んでおくと、同じ部品を2回使ったページで意図しない側にフォーカスが当たります。autofocus は再利用する部品の中ではなく、ページ側で指定するのが安全です。
JavaScript の focus() との違い
同じ「フォーカスを当てる」処理は、JavaScript の element.focus() でも書けます。
const input = document.getElementById('q');
// フォーカスを当てる
input.focus();
// スクロールさせずにフォーカスだけ当てる
input.focus({ preventScroll: true });
2つの方法は、フォーカスが決まるタイミングと、制御できる範囲が違います。
| autofocus | element.focus() | |
|---|---|---|
| 書く場所 | HTML の属性 | JavaScript |
| タイミング | HTML が読み込まれたとき(ダイアログは開いたとき) | コードを実行した任意のタイミング |
| JavaScript が無効なとき | 動作する | 動作しない |
| 条件分岐 | できない(属性を出力するかどうかで切り替える) | できる |
| 後から追加した要素 | 期待どおりに動かないことがある | 確実に当てられる |
| スクロールの抑制 | 指定できない | preventScroll: true で指定できる |
使い分けの基準はシンプルです。最初から HTML に書かれている要素へ、ページを開いた時点で当てたいだけなら autofocus。属性を1つ足すだけで済み、スクリプトの読み込み完了を待つ必要もありません。
一方、入力チェックでエラーが出た欄にフォーカスを戻す、モーダルを閉じたあとに元のボタンへ戻す、タブを切り替えたら先頭の要素へ移すといった、状況に応じて移動先が変わる処理は focus() の担当です。autofocus は HTML を読み込む時点で対象が決まってしまうため、あとから変えることはできません。
使う前に考えたいアクセシビリティの問題
autofocus は便利な反面、ユーザーの意思とは関係なくページの状態を変えてしまう属性でもあります。どのページにも付けてよいものではありません。
スクリーンリーダーが冒頭を読み飛ばす
スクリーンリーダーは通常、ページの先頭から順に読み上げます。ところが autofocus でページ中ほどの入力欄にフォーカスが移ると、読み上げの開始位置もそこへ飛びます。結果として、見出しや説明文といった「ここが何のページなのか」を伝える情報を聞かないまま、いきなり入力欄に立たされることになります。
目で見ていれば周囲の文脈が一瞬で分かりますが、音声で順番に聞いている人にとっては、自分がどこにいるのか把握できない状態です。フォームの目的や入力の注意事項が冒頭に書かれているページほど、影響は大きくなります。
スマートフォンでキーボードが開く
テキスト入力欄にフォーカスが当たると、スマートフォンではソフトウェアキーボードがせり上がってきます。画面の半分近くがキーボードで埋まり、本来見えるはずだった説明文やボタンが隠れてしまうわけです。
ユーザーがまだ読むつもりでいた段階でキーボードが出てくるのは、単純に邪魔です。閉じるにはキーボードの外側をタップするなど、余計な操作も必要になります。入力を始める気がないうちは開かないほうが親切です。
ページが勝手にスクロールする
フォーカスが当たった要素は画面内に表示される必要があるため、ブラウザーはその位置まで自動的にスクロールします。ページの下のほうにある問い合わせフォームに autofocus を付けると、ページを開いた瞬間にファーストビューを飛び越えて、フォームまで一気にスクロールすることになります。
訪問者からすれば、何も操作していないのにページが動いた状態です。記事や説明を読ませたいページでこれをやると、内容を読んでもらう機会そのものを失いかねません。
判断の基準は「入力がページの主目的か」
これらを踏まえた使いどころは、はっきりしています。そのページに来た人が、ほぼ確実に、真っ先にその要素へ入力するときだけ使ってください。検索専用ページ、ログイン画面、単独の問い合わせフォーム、開いた瞬間に入力を求めるダイアログ。いずれも「入力すること自体がページの目的」です。
逆に、記事ページのサイドバーにある検索窓、トップページの下部にあるメルマガ登録欄、記事末尾のコメント投稿欄などは、ページの主目的ではありません。こうした場所への autofocus は、利便性より不便のほうが大きくなります。迷ったときは付けない、と決めておいて構いません。
autofocus が効かないとき
書いたはずなのにフォーカスが当たらない。原因はいくつかのパターンに分かれます。
要素が非表示になっている
フォーカスは、画面に表示されている要素にしか当たりません。hidden 属性が付いている、CSS で display: none や visibility: hidden が効いている、といった要素に autofocus を書いても何も起こりません。
タブ切り替えやアコーディオンの中に入力欄を置いている場合、閉じている側は非表示です。開いた時点でフォーカスさせたいなら、表示を切り替える処理のあとに focus() を呼ぶ形にしてください。
要素が disabled になっている
disabled が付いた要素はフォーカスを受け取れません。<input disabled autofocus> と書いても、disabled が優先されて無視されます。同意チェックを入れるまでボタンを disabled にしておくような作りでは、有効化したあとに focus() で移すことになります。
ちなみに readonly は事情が違い、こちらは入力を受け付けないだけでフォーカス自体は当たります。両者を混同しないよう注意してください。
ページ内に autofocus が複数ある
前述のとおり、採用されるのは文書内で最初に現れた1つだけです。「思った要素とは別のところにフォーカスが当たる」ときは、まずページ全体を検索してみてください。
// autofocus が付いた要素をすべて列挙する
console.log(document.querySelectorAll('[autofocus]'));
// いま実際にフォーカスされている要素を確認する
console.log(document.activeElement);
CMS やテンプレートエンジンを使っていると、共通パーツ側に autofocus が仕込まれていて自分では書いた覚えがない、というケースもあります。document.activeElement でどこに当たっているかを確かめるのが近道です。
あとから JavaScript がフォーカスを奪っている
autofocus でいったんフォーカスが当たっても、そのあとに動いたスクリプトが別の要素へフォーカスを移せば、当然そちらが勝ちます。スライダーやモーダル、同意バナーといったライブラリーが、初期化時にフォーカスを操作していることは珍しくありません。
切り分けたいときは、該当のスクリプトを一時的に読み込まない状態で試してみてください。それでフォーカスが当たるなら、原因はスクリプト側です。
要素をあとから JavaScript で追加している
これは SPA やフレームワークを使っているときに起きがちです。autofocus は HTML が読み込まれる過程で処理されるため、ページ表示後に JavaScript で挿入した要素では、期待どおりに動かないことがあります。文字列として autofocus を含む HTML を innerHTML で流し込んでも、フォーカスは移りません。
// 属性を書いても当てにできない
container.innerHTML = '<input type="text" autofocus>';
// 挿入したあとに focus() を呼ぶ
container.innerHTML = '<input type="text" id="new-input">';
document.getElementById('new-input').focus();
動的に描画される画面では、属性に頼らず focus() を明示的に呼ぶと覚えておけば間違いありません。
iframe の中に書いている
埋め込んだ <iframe> の中の autofocus は、素直に動くとは限りません。ブラウザーによっては、別ドメインの iframe 内からのフォーカス移動を制限しているためです。広告や埋め込みフォームが勝手に親ページのスクロール位置を動かすのを防ぐ、という意図があります。
挙動はブラウザーや条件によって差があるので、iframe を使う構成では autofocus が効くことを前提にしないでください。どうしても必要なら、iframe 内のスクリプトから focus() を呼ぶ方法を検討します。
まとめ
autofocus は、フォーカスを当てたい要素に属性を1つ書くだけで、ページを開いた時点でその要素へフォーカスを移せる属性です。真偽値属性なので値は不要で、autofocus="false" と書いても無効にはなりません。オフにしたいときは属性ごと出力しないようにします。
グローバル属性なので input に限らず、textarea や select、button、contenteditable な要素、tabindex でフォーカス可能にした要素にも使えます。<dialog> を showModal() で開いたときの初期フォーカスを指定する用途にも有効です。1つの文書で有効なのは最初の1つだけ、という点も押さえておきましょう。
そして何より大切なのが、使う場面を選ぶことです。読み上げの文脈が飛ぶ、スマートフォンでキーボードが画面を覆う、勝手にスクロールするといった副作用があるため、入力することがそのページの主目的であるときに限って使ってください。条件によって切り替えたい場合や、あとから挿入した要素には element.focus() を使う。この住み分けができていれば、autofocus は使いどころのはっきりした頼れる属性になります。