7

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.

1 Answer 1

11

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(() => { ... })
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks this example now allows me to select the element however it isn't scrolling to the bottom of the element. Any idea why? Nothing seems to happen with no errors.
I have also tried using container.scrollTop = 0 for the sake of simplicity and this doesn't work either. I have used Vue.nextTick like you have mentioned.
Try console.log-ing out the values of container.scrollTop and container.scrollHeight, check to see what they are. Also make sure that the #conversation__tram-x element is the actual element you want to scroll.
Yes that element is the one I'd like to scroll to. I have checked the values, scrollTop is equal to 0 and scrollHeight is equal to 296. If I console the container variable it is the element that I expect it to scroll to.
Wait, you expect it to scroll to the container element? Currently you are trying to scroll the inner contents of the container element itself. If you want to scroll the container parent to show the container element, you will need to get it's parent element and scroll that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.