Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

330
Views
Vue js - ref within v-for loop always returning null

I am trying to access an element in Vue as I want to see when it's class changes (I'm using Bootstrap so I want to watch for when a class changes to shown). The problem is that as soon as I put a ref'd element into a v-for loop it always returns null. I have tried appending the ref's name with the index of the loop item, and then trying to log this to the console it just always returns 'null'.

As soon as I take the element out of the v-for loop it returns the element fine.

I can't seem to find a solution. Is this something that Vue just simply doesn't support? If not, is there a better way for me to watch the DOM to see if an element's class changes?

P.S. I'm using the composition API.

Template:

<template>
    <div v-for="room in rooms" :key="room.id" class="">
        <p class="test" :ref="'test'+room.id">Hello world</p>
    </div>
</template

Script:

setup(){
      const test1 = ref(null)

      onMounted(() => {
          console.log("test1: ", test1.value)
      })

      return { test1 }
}
about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

Template refs in a v-for

There's a known bug (vuejs/core#5525), where array refs are not properly populated.

One workaround is to bind a function that pushes the element refs into the array:

                       👇
<p class="test" :ref="el => roomRefs.push(el)">Hello world</p>

demo 1

Watching class changes

To observe changes to the class attribute of the elements, use a MutationObserver.

mounted hook: For each roomRefs item:

  1. Create a MutationObserver with a callback that handles mutations (i.e., a change to the observed attribute).

  2. Use that MutationObserver instance to observe the class attribute on the roomRefs item.

  3. Collect each observer into an array, as we'll need to disconnect the observer upon unmounting.

unmounted hook:

  1. disconnect all observers to stop watching for changes. This cleanup step is necessary to avoid a memory leak.
export default {
  props: {
    rooms: {
      type: Array,
      required: true,
    },
  },
  setup() {
    const roomRefs = ref([])
    let observers = []

    onMounted(() => {
      1️⃣
      observers = roomRefs.value.map(roomEl => {
        const observer = new MutationObserver(mutations => {
          for (const m of mutations) {
            const newValue = m.target.getAttribute(m.attributeName)
            console.log('class changed', { roomEl, newValue, oldValue: m.oldValue })
          }
        })
        2️⃣
        observer.observe(roomEl, {
          attributes: true,
          attributeOldValue: true,
          attributeFilter: ['class'],
        })
        3️⃣
        return observer
      })
    })
    4️⃣
    onUnmounted(() => observers.forEach(obs => obs.disconnect()))
  }
}

demo 2

about 4 years ago · Juan Pablo Isaza Report

0

This looks like a pretty common problem, so I went digging through the Vue source and found the following:

It's a limitation of the way Vue does updates and renders. If you have multiple s for the same template, then each of them has its own patch queue. Each patch queue gets processed on its own schedule, so it's possible for one to finish before another even starts. If that happens, then one of the queued updates won't happen. That would mean Vue wouldn't detect changes to your element and hence wouldn't do any re-rendering at all, which is why your element doesn't show up in your console output!

You can work around this by making sure all update queues finish before each other begins, which we can do by adding an update: { sync: true } config to your component in question. This will force Vue to process all of your update queues synchronously before processing any other changes in the application.

You could also add a sync: true config to your parent's component that wraps all of your other components and make sure they all sync.

<script>
  setup() {
    const test1 = ref(null)

    onMounted(() => {
      console.log("test1: ", test1.value)
    })

    return { test1 }
  }

  render() {
      return (
        <div>Hello World</div>
      )}

  </script>

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!