🙂 はじめに
こんにちは。
私が開発・サーバー運用を担当している「XD.GROWTH」ですが、お客様のWebサイトにタグを埋め込み、訪問者の行動(ページ閲覧・クリック・カート投入など)を捕捉して分析・施策につなげる、というよくあるタイプのMAツールです。
先日、お客様の1社から、
「Safari でカート操作対象の商品情報が上手く取れない場合がある」
というご連絡を受けて調査したところ、原因は Safari で <a> タグのクリック時に focus イベントが発火しない ことでした。
「あー、そういえば昔も radio / checkbox で同じことにハマったなぁ……」と思い出したのですが、要素ごとの focus 発生可否をまとめた一覧表ってあまり見かけないなと感じたので、自分用のチートシートも兼ねて軽い小ネタとして記事にしてみました。
「いつ focus が発生して、いつ発生しないんだっけ?」が一目で分かる、というのが本記事のゴールです。
本記事のスコープ
あくまで一覧表(チートシート)として参照することを目的としており、各挙動の詳細な仕様根拠(WebKit のソースレベルでの解説など)には踏み込みません。実装で迷ったときにサッと開ける、その程度の軽さで使ってもらえればと思います。
⚙️ 動作確認環境(参考)
| 項目 | バージョン |
|---|---|
| macOS Safari | 17.x 系で確認 |
| iOS Safari | iOS 17.x 系で確認 |
挙動の傾向自体は Safari 16 以前から大きく変わっていませんが、<button> の focus 挙動だけは Safari 17 / iOS 17 で仕様変更があるため、後述の表でそれぞれ補足しています。
🧭 大原則
細かい表に入る前に、Safari の focus 挙動を理解する上での大原則を 1 行で書いておきます。
テキスト編集可能な要素のみ、クリック / タップで focus する
これは Safari が macOS の Human Interface Guidelines に従っているためで、「アクション系・選択系のコントロールはユーザーのフォーカスを奪わない」というネイティブ UI 慣習を Web にもそのまま持ち込んでいる、と理解すると腑に落ちます。
逆に言えば、「テキストを編集する目的ではない要素」は、クリック / タップしても focus イベントが発火しません。これが今回のチートシートの根っこにあるルールです。
🖥️ macOS Safari:クリック時の focus 発生状況
| 要素 | クリックで focus 発生? | 補足 |
|---|---|---|
<input type="text"> / email / password / search / tel / url
|
✅ | テキスト編集可能なので発火 |
<input type="number"> / date / time / datetime-local / month / week
|
✅ | 内部的にテキスト入力を伴うので発火 |
<textarea> |
✅ | テキスト編集可能なので発火 |
<select> |
✅ | ドロップダウン展開と同時に focus |
<button> / <input type="button"> / submit / reset
|
⚠️ | Safari 17 以前:発火しない / Safari 17 以降:発火する |
<input type="radio"> |
❌ | クリックでは発火しない(Tab 移動時のみ発火) |
<input type="checkbox"> |
❌ | クリックでは発火しない(Tab 移動時のみ発火) |
<input type="file"> |
⚠️ | クリックでファイル選択ダイアログが開く挙動が優先される |
<input type="color"> / range
|
⚠️ | バージョン・操作によって挙動が変わる |
<a>(リンク) |
❌ | クリックでは発火しない |
contenteditable 要素 |
✅ | テキスト編集可能なので発火 |
tabindex 付き任意要素 |
❌ | クリックでは発火しない(Tab 移動時のみ発火) |
🔁 Tab 移動時の focus 発生状況(補足)
クリックでは focus しなくても、Tab 移動なら focus する要素も多いです。
| 要素 | Tab で focus 発生? |
|---|---|
| テキスト系 input / textarea / select | ✅ |
| button / radio / checkbox | ✅ |
<a>(リンク) |
⚠️ デフォルトでは Tab 巡回されない |
tabindex="0" 付き要素 |
✅ |
<a> の Tab 巡回について
Safari は macOS の慣習に従い、デフォルトでは Tab キーで「フォームコントロールのみ」を巡回します。<a> を巡回対象に含めたい場合は、以下のいずれかが必要です。
- Safari の環境設定 → 詳細 →「Tab キーを押した時に Web ページ上の各項目を強調表示」を ON にする
-
Option + Tabで操作する
📱 iOS Safari:タップ時の focus 発生状況
iOS Safari も macOS Safari と同じ大原則に従いますが、タッチ UI ならではの違いがいくつかあります。
| 要素 | タップで focus 発生? | 補足 |
|---|---|---|
<input type="text"> / email / password など |
✅ | ソフトウェアキーボードが表示される |
<textarea> |
✅ | ソフトウェアキーボードが表示される |
<input type="number"> / date / time など |
✅ | 専用の入力 UI(数字キーパッド・ホイールピッカー等)が表示される |
<select> |
✅ | ネイティブピッカーが表示される |
<button> / <input type="button"> / submit
|
⚠️ | iOS 16 以前:発火しない / iOS 17 以降:発火する |
<input type="radio"> |
❌ | 状態は切り替わるが focus は移らない |
<input type="checkbox"> |
❌ | 状態は切り替わるが focus は移らない |
<a>(リンク) |
❌ | タップで遷移するが focus は持続しない |
contenteditable 要素 |
✅ | テキスト編集可能なので発火 |
tabindex 付き任意要素 |
❌ | タップでは発火しない |
⌨️ 外付けキーボード接続時の Tab 移動
iPad などに外付けキーボードを接続している場合、Tab キーによる移動が可能になり、macOS Safari と概ね同じ挙動になります。<a> を Tab で巡回したい場合は、iPadOS の「フルキーボードアクセス」設定を有効にする必要があります(設定 → アクセシビリティ → キーボード → フルキーボードアクセス)。
プログラムからの .focus() 呼び出し制約
iOS Safari では、element.focus() を JavaScript から呼んでも、ユーザー操作起因のイベントハンドラ内でないとソフトウェアキーボードが立ち上がらない という制約があります。setTimeout や Promise の解決後に呼び出してもキーボードが出ない、というハマりがあるので、テキスト入力欄に自動フォーカスさせたい場合は注意してください。
🛠️ では、どうするの? focus に頼らない実装パターン
「Safari で focus が発火しない」を回避するために focus を無理矢理発火させるよりも、そもそも focus に頼らない実装に置き換える 方が結局のところ堅牢になりやすいです。
ざっくり、こんな選択肢があります。
- ラジオ / チェックボックスの値変更検知 →
changeイベント - 必須項目バリデーション → 親要素の
focusoutイベント で委譲、または submit 時にまとめて検証 - クリック対象の要素特定 →
clickイベント +closest()+dataset - サイト側 click ハンドラより先に動かしたい → capture フェーズ(
addEventListener(..., true)) - ページ遷移直前にログ送信したい →
navigator.sendBeacon() - 動的に追加・削除される要素を追跡したい →
MutationObserver
詳しい使い分けは別の機会に書こうと思いますが、focus を「他の用途に転用するためのフック」として使うのは Safari 環境では成立しないことが多い、と覚えておくと無難です。
🧾 まとめ
- Safari は「テキスト編集可能な要素のみクリック / タップで focus する」が大原則
- radio / checkbox /
<a>はクリック / タップでは focus せず、Tab 移動でのみ focus(<a>は設定次第) -
<button>は Safari 17 / iOS 17 以降で focus 挙動が変わったので要注意 - iOS Safari ではタッチ UI 特有の制約(focus の視覚表現が薄い、外付けキーボードがないと Tab 不可、
.focus()のプログラム呼び出し制約)もある - focus に頼った実装は脆くなりがちなので、
change/click(capture フェーズ)/MutationObserverなどの代替手段も検討すると良い
Safari のクセは「macOS のネイティブ UI 慣習に忠実」というポリシーの裏返しでもあるので、IE のような独自仕様の暴走とは違って思想に一貫性があります。それを踏まえて「クロスブラウザで素直に動く設計」を選びたいですね。
軽い小ネタでしたが、誰かの「あれ、Safari だけ動かない……」のデバッグ時間を少しでも短縮できれば幸いです。