Scenario
Imagine you have multiple clickable fields which have icons. You want to focus the input field no matter where you click in the parent element.
Problem
Depending on where you click, the $event.target
changes. This means that you either have to use a clever way to find the intended target or take an elegant approach.
Vue2 Solution
The most elegant approach is to define a ref and access the ref corresponding to the element being clicked.
In Vue2 this would look like the following.
<template>
<div @click="clickMe('f1')">
<input ref="f1" />
<Icon/>
</div>
<div @click="clickMe('f2')">
<input ref="f2" />
<Icon/>
</div>
<div @click="clickMe('f3')">
<input ref="f3" />
<Icon/>
</div>
</template>
<script>
export default {
methods: {
clickMe(refId) {
var input = this.$refs[refId]
input?.focus()
}
}
}
</script>
Vue3 Problem
If you are using Vue3 composition API, you'll notice achieving this behavior is not super obvious.
Methods no longer have access to an object which holds all the refs defined in a component. So you may end up settling for a less elegant solution. However, you don't have to.
Vue3 Solution
The solution is simple. Use an object where the property values are refs. Then you could update your template to something like the following.
<script setup>
import { ref } from 'vue';
const inputs = {
f1: ref(),
f2: ref(),
f3: ref(),
}
function clickMe(refId) {
var input = inputs[refId]?.value
input?.focus()
}
</script>
<template>
<div @click="clickMe('f1')">
<input :ref="inputs.f1" />
<Icon/>
</div>
<div @click="clickMe('f2')">
<input :ref="inputs.f2" />
<Icon/>
</div>
<div @click="clickMe('f3')">
<input :ref="inputs.f3" />
<Icon/>
</div>
</template>
Bonus: A Better Way to Define the Refs
Instead of writing ref over and over. You could write a method to define refs from an array. Since I'm using Lodash, I did something like this.
function defineRefs(refs) {
return _.chain(refs).keyBy().mapValues(ref).value();
}
Now I can rewrite my inputs
definition like this:
<script setup>
import { defineRefs } from '@/helpers'
const inputs = defineRefs(['f1', 'f2', 'f3'])
//...
</script>
Conclusion
Instead of using an event target to focus an element, you can use dynamic refs. Doing so allows you to reuse event handlers.