How to use a querySelectorAll in VueJS

ghz 1years ago ⋅ 2362 views

Question

I'm trying to trigger [GA events](https://google- analytics.nuxtjs.org/usage/event-tracking) using Nuxt. It's successfully working but it's only returning the first one it runs into because of document.querySelector rather than querySelectorAll. I have tried the spread operator and a for() but still no luck - the compiler just says cannot read property txt of undefined.

Here's my v-for:

<ul>
 <li class="li-item" v-for="item in items" :key="item.id" @click="doGA" :data-txt="item.name">
  {{ item.name }}
 </li>
</ul>



<ListDetails
   :items="[
     {
       name: 'Lorem',
     },
     {
       name: 'Ipsum',
     },
     {
       name: 'Dolor',
     },
   ]"
/>



methods: {
  doGA(){
    this.$ga.event({
       eventCategory: "Ext links",
       eventAction: "click",
       //this.dataset.txt does not work below here either
       eventLabel: document.querySelectorAll('.li-item').dataset.txt;
    });
  }
}

So when a user clicks 'Ipsum' it's returning 'Lorem' in GA and likewise for 'Dolor' it will do the same. It is because I have used document.querySelector rather than selectorAll as I have said above, but I can't get all() to work properly.

As we know querySelector will return this first one the browser finds.


Answer

TLDR: querySelector and querySelectorAll are usually not the way to go in VueJS, if you want to select something. Either bind with state or find it with a this.$refs[...].


With a template looking like this

<ul ref="list">
  <li v-for="item in items" :key="item.id" @click="doGA(item.name)">
    {{ item.name }}
  </li>
</ul>

You could call the method on each of your element like this

methods: {
  doGA(itemName){
    this.$ga.event({
       eventCategory: "Ext links",
       eventAction: "click",
       eventLabel: itemName
    })
  }
}

Otherwise, if you want to send all of them, you can do the following

<template>
  <ul>
    <li v-for="item in items" :key="item.id" @click="doGA">
      <span :ref="`item-${item.id}`" :data-txt="`fancyText-${item.id}`">
        number >> {{ item.id }}
      </span>
    </li>
  <ul>
</template>

<script>
export default {
  data() {
    return {
      items: [
        {
          id: 1,
          name: 'Lorem',
        },
        {
          id: 2,
          name: 'Ipsum',
        },
        {
          id: 3,
          name: 'Dolor',
        },
      ],
    }
  },
  methods: {
    async doGA() {
      for (const item of this.items) {
        console.log('element found', this.$refs[`item-${item.id}`][0].dataset.txt)

        await this.$ga.event({
          eventCategory: 'Ext links',
          eventAction: 'click',
          eventLabel: this.$refs[`item-${item.id}`][0].dataset.txt,
        })
      }
    },
  },
}
</script>

Notes

  • $refs is the way to go in VueJS, rather than querySelector.
  • async/await may be useful if you do care about the order, if you don't and only want to send them, I guess that you can pass on this one.
  • You can remove the spans, I let it for visibility mainly.
  • I used data here, but this should behave pretty much the same with any props.