🍎
iOS / SafariでonBlurが発火しない問題を解決したい (React)
対象
Safariブラウザのみで発生する 「onBlurが発火しない問題」 の解決方法についてお話しします。
(Reactを対象にお話ししますが、問題の原因と解消方法自体は他のフレームワークでも参考に頂けるかと。)
問題
下記の様にonBlurで何か処理をしている場合に、Safariブラウザでのみ上手く動かない。。。という問題に直面しました。
const SomethingComponent: React.FC = () => {
const [isActive, setIsActive] = useState(false)
return (
<button
onClick={() => {
console.log('set active')
setIsActive(true)
}}
onBlur={() => {
console.log('set inactive')
setIsActive(false)
}}
>
{isActive ? 'アクティブ' : '非アクティブ'}
</button>
)
}
結果
🙆♂️Chrome | 🙅Safari |
---|---|
原因
Safariでは、Focusが発火しない模様。
ワークアラウンド
下記の様にコンポーネントのクリックハンドラーに要素を強制的にフォーカスさせる処理を追加することで、
Safariブラウザでもblurイベントを検知できるようになります。
(ちょっとまだ挙動は異なりますが、色々調整すれば要件に沿った挙動は満たせるかと)
const SomethingComponent: React.FC = () => {
const ref = useRef<HTMLButtonElement>(null)
const [isActive, setIsActive] = useState(false)
return (
<button
ref={ref}
onClick={() => {
console.log('set active')
ref.current?.focus()
setIsActive(true)
}}
onBlur={() => {
console.log('set inactive')
setIsActive(false)
}}
>
{isActive ? 'アクティブ' : '非アクティブ'}
</button>
)
}
補足
下記の様に、クリックイベント発火時に、強制的にフォーカスを当てる処理を追加したラッパーを作成し、
ボタンコンポーネントの代わりに使うことで、影響範囲少なく使えるかと思います。
import { ButtonHTMLAttributes, useRef } from 'react'
const BlurButton: React.FC<ButtonHTMLAttributes<HTMLButtonElement>> = (props) => {
const ref = useRef<HTMLButtonElement>(null)
return (
<button
ref={ref}
{...props}
onClick={(e) => {
ref.current?.focus() // focusイベントを強制発火
props?.onClick && props.onClick(e)
}}
>
{props.children}
</button>
)
}
export default BlurButton
実際の動いているアプリの挙動は下記の集合駅検索サービスKokone
上で確認できます!
Discussion