Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js can't find element using querySelector

I am trying to create a chat style form. So a user inputs their data and then uses the button within my template with the class of continue-btn.

As you can see when the continue-btn is pressed it uses the nextStep method which adds 1 to the counter data property.

Within my template I then use v-if="counter >= 1" to display the next section of the chat dialog and input field.

I am then trying to use scrollTop to automatically scroll the page to the new section with the id of #conversation__tram-1. I originally tried running this block of code just after the counter had been given a value of 1:

const container = this.$el.querySelector("#conversation__tram-" + this.counter);
container.scrollTop = container.scrollHeight;

This didn't work though because I'm guessing the #conversation__tram-1 element hadn't been added to the DOM yet.

So for the sake of testing I tried wrapping it in a timeout function:

  setTimeout(function(){
    const container = this.$el.querySelector("#conversation__tram-" + this.counter);
    container.scrollTop = container.scrollHeight;
  }, 3000);

However I am left with this error when trying this:

Uncaught TypeError: Cannot read property 'querySelector' of undefined

Here is my whole single vue file:

<template>

    <div id="conversation-app">
      <!-- <div v-for="item in items">
        {{ item.text }}
      </div> -->
      <div class="conversation__track">
        <div id="conversation__tram-0">
          <div class="conversation__item agent">
            <img src="/assets/cdn.annuityadvicecentre.dev/images/theme-f/michael-chat-agent.jpg" class="conversation__item-prof-img" alt="Michael Chat Agent" />
            <div class="conversation__item-content">
              <p>
                Hello my name is {{ agent }}, we'll compare the whole annuity market to bring you back the best annuity rates from the top providers for you. Let's get started, what's your name?
              </p>
            </div>
          </div>
          <div class="conversation__item customer" id="title-fullname">
            <div class="conversation__item-content">
              <p>
                Hi {{ agent }}, my name is...
              </p>
              <div class="row">
                <div class="col-4">
                  <select id="title" class="field-title" name="payload[title]"><option value="mr">Mr</option><option value="mrs">Mrs</option><option value="miss">Miss</option><option value="ms">Ms</option></select>
                </div>
                <div class="col-8">
                  <input v-model="customerName" id="full_name" class="field-full_name" name="payload[full_name]" type="text">
                </div>
              </div>
            </div>
          </div>
        </div>
        <transition name="fade">
          <div id="conversation__tram-1" v-if="counter >= 1">
            <div class="conversation__item agent">
              <img src="/assets/cdn.annuityadvicecentre.dev/images/theme-f/michael-chat-agent.jpg" class="conversation__item-prof-img" alt="Michael Chat Agent" />
              <div class="conversation__item-content">
                <p>
                  Thanks {{ firstName }}, nice to meet you. To process your instant quote please can I have your Pension Value?
                </p>
              </div>
            </div>
            <div class="conversation__item customer">
              <div class="conversation__item-content">
                <p>
                  Sure, my pension value is...
                </p>
                <input id="pension_value" class="field-pension_value" placeholder="£" pattern="\d*" name="payload[pension_value]" type="number">
                <div class="error-wrap error_pension_value is-hidden" data-format="<div class=&quot;error-text&quot;>:message</div>"></div>
              </div>
            </div>
          </div>
        </transition>
        <div id="conversation__buttons">
          <button type="button" class="continue-btn"
          v-on:click="nextStep"
          >Continue&nbsp;<i class="fa fa-chevron-right" aria-hidden="true"></i></button>
        </div>
      </div>
    </div>
</template>

<script>
export default {
  name: 'conversation-app',
  data () {
    return {
      agent: 'Brick',
      counter: 0,
      customerName: '',
    }
  },
  methods: {
    nextStep: function() {
      this.counter += 1;
      setTimeout(function(){
        const container = this.$el.querySelector("#conversation__tram-" + this.counter);
        container.scrollTop = container.scrollHeight;
      }, 3000);

    },
  },
  computed: {
    firstName() {
      return this.customerName.split(' ')[0];
    }
  }
}
</script>

Any idea why this isn't working? Thanks.

like image 376
Nick Maddren Avatar asked May 30 '17 09:05

Nick Maddren


People also ask

Can I use querySelector in Vue?

querySelector in VueWe can use querySelector to select an HTML element that returns the first element within the document that matches a specified selector. It can be used to set, obtain, modify, or delete properties of the selected element in Vue.

Can you use querySelector on an element?

The querySelector() method is available on the document object or any Element object.

How do I reference an element in Vue?

If we want to reference a DOM element, we can use the ref attribute in Vue. Vue can store references to DOM elements in a property called $ref . The first thing we have to do, is add a ref attribute to the element we want to reference in our Javascript.

What is the difference between Get element by ID and querySelector?

With a querySelector statement, you can select an element based on a CSS selector. This means you can select elements by ID, class, or any other type of selector. Using the getElementById method, you can only select an element by its ID. Generally, you should opt for the selector that most clearly gets the job done.


1 Answers

This is a good time to use arrow functions, as they preserve the context of this.

nextStep: function() {
  this.counter += 1;
  setTimeout(() => {
    const container = this.$el.querySelector("#conversation__tram-" + this.counter);
    container.scrollTop = container.scrollHeight;
  }, 3000);

Altenatively, instead of the timeout you can use Vue.nextTick which is a more technically-correct way of doing this.

nextStep: function () {
  this.counter += 1
  this.$nextTick(() => { ... })
like image 120
varbrad Avatar answered Oct 22 '22 11:10

varbrad