我昨天才开始用
vue.js
编码,我不知道如何在不使用“传统”JS方式的情况下“聚焦”文本框,即document.getElementById('myTextBox').focus()
。
最初,我的文本框是隐藏的。我有一个“开始”按钮,当用户单击它时,会显示文本框,可以这么说,我想在那里设置
focus
。我已经尝试使用 ref
,但无济于事(请参阅下面的代码)。
HTML:
<input id="typeBox" ref="typeBox" placeholder="Type here..." />
Javascript
export default {
name: 'game',
methods: {
startTimer () {
setTimeout(function () { /* .focus() won't work without this */
/* ugly and not recommended */
// document.getElementById('typeBox').focus()
/* Throws the error: Cannot read property 'typeBox' of undefined */
this.$refs.typeBox.focus()
// ... any other options?
// ...
}, 1)
}
} /* END methods */
} /* END export default */
有人知道该怎么做吗?请帮忙。
更新:
在
autofocus
上添加 input
可以在页面加载后立即聚焦。但在我的应用程序中,需要多次“重新聚焦”输入字段而不重新加载页面,这就是为什么我需要一种方法来调用 .focus()
。
在此分享解决方案,以防万一有人遇到同样的问题...
我终于在一位高级程序员的帮助下解决了这个问题。我还能够使用其
setTimeout
版本 vue
一路消除 nextTick()
。
正确的JS代码:
startTimer () {
this.$nextTick(() => {
// this won't work because `this.$refs.typeBox` returns an array
// this.$refs.typeBox.focus()
//this one works perfectly
this.$refs.typeBox[0].focus()
})
} /* END startTimer */
说明:
当我使用
console.log(this.$refs.typeBox)
时,它返回这个数组:
这就是为什么代码要工作,它必须是
typeBox[0].focus()
而不是 typeBox.focus()
。
this
函数中setTimeout
的值将被设置为window
对象,因为它是一段时间后执行的回调函数,并且它已经失去了this
关键字的范围,该范围是从动态设置的正在调用函数。
箭头函数不绑定它自己的
this
值。
startTimer () {
setTimeout(() => {
this.$refs.typeBox.focus()
}, 1)
}
或
startTimer () {
const self = this;
setTimeout(function () {
self.$refs.typeBox.focus()
}, 1)
}
终于解决了没有
setTimeout
的问题,感谢window.requestAnimationFrame
(不知道为什么):
startTimer () {
window.requestAnimationFrame(() => this.$refs.typeBox.focus())
}
它甚至适用于自定义组件聚焦。
在我的例子中,
nextTick
不起作用,因为由于某种原因,反应值(例如visible
布尔模型)更改和子组件安装之间存在相当明显的随机延迟。我没有利用 setTimeout
,而是使用这种“动作队列”模式解决了它:
<script setup lang="ts">
import Button from './ui/button/Button.vue'
const visible = defineModel<boolean>({ default: false })
const sidebarFocus = ref<InstanceType<typeof Button> | null>(null)
const actionQueue = ref<(() => void)[]>([])
watch(visible, (value) => {
if (value) {
actionQueue.value.push(() => {
if (sidebarFocus.value)
sidebarFocus.value.$el.focus() // Don't forget the `$el`
})
}
})
watch(sidebarFocus, (value) => {
if (value) {
actionQueue.value.forEach(action => action())
actionQueue.value = []
}
})
</script>
<template>
<div v-if="visible" class="my-sidebar">
<Button ref="sidebarFocus" @click="visible = false">
Close <!-- This button gets focused each time the sidebar opens -->
</Button>
</div>
</template>
基本上,
visible
的观察者将回调推送到数组,然后在按钮安装时调用该回调,并清空队列(以防止无限循环)。回调本身只是将焦点放在按钮上。