Skip to the solution
MediumFrontend9 min read

How do you access a DOM element or child component instance?

720 views

The problem

Question presented to candidate: "You need to focus an input when a page opens, measure a chart container for a third-party library, and call reset() on a child form component. How do you get hold of the DOM element and the child component in Vue 3, and when are they actually available?"

What a strong answer should cover:

  • Template refs: put ref="name" on an element or component; in <script setup> read it through a matching ref(null) or, since Vue 3.5, useTemplateRef("name"); in the Options API through this.$refs.name.
  • Timing: the ref is null during setup and the first render, filled before onMounted runs, and set back to null when the element unmounts (for example behind v-if), with the DOM change applied after nextTick.
  • On a component, the ref holds the child's public instance, not a DOM node: a <script setup> child shows only what it lists in defineExpose, plus built-ins such as $el.
  • v-for refs collect an array (order not guaranteed to match the source), and function refs (:ref="el => ...") give full control but are called on every update.
  • Prefer declarative tools first; use refs for focus, measurement, scrolling, media and third-party libraries.
  • $el on a multi-root component is a placeholder text node, so expose a method or ref the real element instead.

Clarifying questions expected:

  • "Is the element always rendered, or is it behind a v-if or a v-for?"
  • "Is the child component a <script setup> component, and does it expose reset?"

Code / implementation expected: Yes β€” an element ref used in onMounted, and a component ref calling an exposed method.

  • refs
  • template

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 9 sections

Read the code

VueElement, component, v-for and v-if refs with their timing logged
<script setup>
import { ref, onMounted, nextTick, defineComponent, h } from "vue";

// A child component that exposes one method to its parent.
const Counter = defineComponent({
  setup(_, { expose }) {
    const count = ref(0);
    expose({ reset: () => (count.value = 0) });
    return () => h("button", { class: "counter", onClick: () => count.value++ }, `clicked ${count.value}`);
  },
});

const input = ref(null);     // matches ref="input" in the template
const counter = ref(null);   // a component ref: the child public instance
const items = ref(["a", "b", "c"]);
const itemEls = ref([]);     // a ref inside v-for collects an array
const show = ref(true);
const box = ref(null);

console.log("setup: input.value =", input.value);

onMounted(() => {
  console.log("mounted: input is", input.value.tagName);
  console.log("mounted: counter exposes", Object.keys(counter.value));
  console.log("mounted: itemEls", itemEls.value.map((el) => el.textContent).join(","));
  input.value.focus();
  console.log("focused:", document.activeElement === input.value);
});

async function hide() {
  show.value = false;
  console.log("right after show = false, box is", box.value?.tagName);
  await nextTick();
  console.log("after nextTick, box is", box.value);
}
</script>

<template>
  <input ref="input" placeholder="focus me" />
  <Counter ref="counter" />
  <button class="reset" @click="counter.reset()">reset child</button>
  <ul>
    <li v-for="item in items" :key="item" ref="itemEls">{{ item }}</li>
  </ul>
  <p v-if="show" ref="box">box</p>
  <button class="hide" @click="hide">hide box</button>
</template>
VueOptionsRefs.vue: the same with this.$refs in the Options API
<script>
// Options API: every ref="name" in the template lands on this.$refs.
export default {
  data: () => ({ rows: ["a", "b", "c"], log: "" }),
  mounted() {
    this.$refs.input.focus();
    this.log = `input=${this.$refs.input.tagName} rows=${this.$refs.row.length} focused=${document.activeElement === this.$refs.input}`;
  },
};
</script>

<template>
  <input ref="input" />
  <p v-for="r in rows" :key="r" ref="row">{{ r }}</p>
  <output>{{ log }}</output>
</template>

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 39 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextMedium

How do you handle forms and validation in Vue?

Continue