简介
主动调用 input.focus()
可以替用户主动锁定输入框并弹出输入法,引导用户填写表单。
但是在实际的使用中,页面有时并不会按照我们的预想去执行。本文将列举几个限制条件,并给出解决方案。
针对于下列问题场景,我写了一个简易的在线DEMO供大家亲自试验:点击查看
IOS Safari浏览器下必须由用户点击触发
在IOS Safari浏览器下要求 input.focus()
的调用必须位于onclick
的回调函数中,且同步执行,例如:
// 点击btnElement即可立即focus【有效】
btnElement.addEventListener('click', function() {
input.focus()
})
反面教材一
在其他位置调用是无效的,例如:
// 页面加载完后立即focus:【IOS Safari浏览器下无效】
window.addEventListener('load', function() {
input.focus()
})
如果你确实要实现这样的功能,可以尝试使用 autofocus
属性
// 页面加载完后立即focus:【有效】
Username: <input type="text" autofocus
placeholder="来都来了就留下大名吧"
/>
autofocus
可以在页面加载完成后自动focus相应的表单组件,但限制是一个页面只能有一个元素设置该属性。
反面教材二
在点击回调函数中异步调用是无效的,例如:
// 点击btnElement后异步调用focus【IOS Safari浏览器下无效】
btnElement.addEventListener('click', function() {
setTimeout(() => input.focus(), 200)
})
常见的异步调用有 setTimeout
、Promise
。IOS Safari浏览器下,包裹其中的 input.focus()
都是无效的。
目标<input>
元素必须可见
这里说的可见是指当 input.focus()
调用时要满足以下条件:
- 目标
<input>
元素已渲染到dom上 - 目标
<input>
元素不是display: none;
- 目标
<input>
元素不是visibility: hidden;
这种问题常见于调起一个弹框界面,其中元素恰好位于弹框内,而我们在元素还未可见就调用了 input.focus()
。尤其是在React或者Vue等现代框架中,我们通过设置状态控制弹框的显隐,这种改变反应到dom上并不是同步的,例如:
const LoginModal: React.FC<{}> = () => {
const [show, setShow] = useState(false)
const ref = useRef<HTMLInputElement>(null)
const handleClick = () => {
// Modal弹框真正被渲染其实是异步发生的
setShow(true)
// 这个时候 input 很可能并未加载或者不可见 【无效】
ref.current?.focus()
}
return (
<div className="login-modal-comp">
<button onClick={handleClick}>Show Modal</button>
<Modal centered visible={show}>
Username: <input ref={ref} type="text" placeholder="来都来了就留下大名吧" />
</Modal>
</div>
)
}
如果这个恰好是你想要实现的功能,你同样可以借助 autofocus
:
<Modal centered visible={show}>
Username: <input ref={ref} autoFocus
type="text" placeholder="来都来了就留下大名吧" />
</Modal>
- 注意在React中为驼峰命名:
autoFocus
<input>
元素会在重新出现时自动聚焦并拉起输入框,亲测有效。