Open5
iOS で自動フォーカス時に仮想キーボードが表示されない件の検証結果
時折、仮想キーボードを表示させるためにプログラムでフォーカスを制御するケースがあります。しかし、iOS のブラウザ(e.g. Mobile Safari)はプログラムで制御されたフォーカス時に仮想キーボードを表示しません。本挙動はバグではなく開発者(Apple)が意図的に行なっているかと思います。Apple は WebKit のバグチケット「Autofocus on text input does not show keyboard」に次のように言及しています。
We (Apple) like the current behavior and do not want programmatic focus to bring up the keyboard
我々 Apple は現在の挙動を好み、キーボードを表示させるプログラムのフォーカスを望んでいません。
https://bugs.webkit.org/show_bug.cgi?id=195884#c4
検証結果、次の仮説を立てます。
ユーザーの動作を伴うイベントハンドラからの focus()
実行は仮想キーボードが表示されます。ただし、フォーカスの対象要素が視覚的に非表示の場合この限りではありません。useEffect()
や setTimeout()
のようなユーザーの動作を伴わないコールバック関数内での focus()
実行は仮想キーボードが表示されません。
検証 1. マウント時に自動フォーカスを発火させる方法
結果:❌ 仮想キーボードは表示されません。
useAutoFocus.ts
import { useEffect, useRef } from 'react';
export const useAutoFocus = <RefType extends HTMLElement>() => {
const ref = useRef<RefType>(null);
useEffect(() => {
const node = ref.current;
if (node) {
node.focus();
}
}, []);
return ref;
}
input.tsx
export const Input = () => {
const { ref } = useAutoFocus<HTMLInputElement>();
return (
<input
type="text"
ref={ref}
/>
)
}
検証 2. イベントハンドラで自動フォーカスを発火させる方法
結果:⭕️ 仮想キーボードは表示されます。
input.tsx
export const Input = React.forwardRef((props, ref) => {
return (
<input
type="text"
ref={ref}
/>
)
})
index.tsx
import { createRef } from 'react';
import { Input } from 'input';
const Index = () => {
const ref = useRef<HTMLInputElement>();
const handleClick = () => {
if (ref.current) ref.current.focus();
}
return (
<Input ref={ref} />
<button type="button" onClick={handleClick}>AUTO FOCUS</button>
)
};
検証 3. デフォルトで非表示の要素に対してイベントハンドラで自動フォーカスを発火させる方法
結果:❌ 仮想キーボードは表示されません。
index.tsx
import { createRef, useState } from 'react';
import { Input } from 'input';
const Index = () => {
const [isVisible, setVisibility] = useState(false);
const ref = useRef<HTMLInputElement>();
const handleClick = () => {
setVisibility(true);
if (ref.current) ref.current.focus();
}
return (
<Input isVisible={isVisible} ref={ref} />
<button type="button" onClick={handleClick}>AUTO FOCUS</button>
)
};
検証 4. デフォルトで非表示の要素に対してハックで自動フォーカスを発火させる方法
結果:⭕️ 仮想キーボードは表示されます。
useAutoFocus.tsx
import { useRef } from 'react';
export const useAutoFocus = <RefType extends HTMLElement>() => {
const ref = useRef<RefType>(null);
const triggerAutoFocus = (timeout = 100) => {
if (!ref.current) return;
const __tempEl__ = document.createElement('input');
__tempEl__.style.position = 'absolute';
__tempEl__.style.top = (ref.current.offsetTop + 7) + 'px';
__tempEl__.style.left = ref.current.offsetLeft + 'px';
__tempEl__.style.height = '0';
__tempEl__.style.opacity = '0';
document.body.appendChild(__tempEl__);
__tempEl__.focus();
setTimeout(() => {
if (!ref.current) return;
ref.current.focus();
ref.current.click();
document.body.removeChild(__tempEl__);
}, timeout);
};
return {
ref,
triggerAutoFocus,
};
};
index.tsx
import { createRef, useState } from 'react';
import { Input } from 'input';
const Index = () => {
const [isVisible, setVisibility] = useState(false);
const {ref, triggerAutoFocus} = useAutoFocus<HTMLInputElement>();
const handleClick = () => {
setVisibility(true);
triggerAutoFocus();
};
return (
<Input isVisible={isVisible} ref={ref} />
<button type="button" onClick={handleClick}>AUTO FOCUS</button>
)
};