What could be causing this Vue 3 app bug where a method is being triggered before the click event even takes place?

I am currently developing a quiz application using Vue 3 and Bootstrap 4.

One issue I encountered is with the function that checks if the selected answer is correct:

verifyAnswer(answer) {
  return answer == this.results[this.questionCount]["correct_answer"];
}

This function should be triggered when a list item is clicked, as shown below:

<li v-for="answer in answers" @click="verifyAnswer(answer)" :class="{'text-white bg-success' : verifyAnswer(answer)}">{{answer}}</li>

If the chosen answer is correct, the list item should have the classes text-white bg-success, otherwise it should have text-white bg-danger.

const quizApp = {
  data() {
    return {
      questionCount: 0,
      results: [
        {
          question: "The book &quot;The Little Prince&quot; was written by...",
          correct_answer: "Antoine de Saint-Exup&eacute;ry",
          incorrect_answers: [
            "Miguel de Cervantes Saavedra",
            "Jane Austen",
            "F. Scott Fitzgerald"
          ]
        },
        {
          question:
            "Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?",
          correct_answer: "Camino Island",
          incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"]
        },
        {
          question:
            "In Terry Pratchett&#039;s Discworld novel &#039;Wyrd Sisters&#039;, which of these are not one of the three main witches?",
          correct_answer: "Winny Hathersham",
          incorrect_answers: [
            "Granny Weatherwax",
            "Nanny Ogg",
            "Magrat Garlick"
          ]
        }
      ]
    };
  },
  methods: {
    nextQuestion() {
      if (this.questionCount < this.results.length - 1) {
        this.questionCount++;
      }
    },
    prevQuestion() {
      if (this.questionCount >= 1) {
        this.questionCount--;
      }
    },
    verifyAnswer(answer) {
      // check if the clicked anwser is equal to the correct answer
      return answer == this.results[this.questionCount]["correct_answer"];
    },
    shuffle(arr) {
      var len = arr.length;
      var d = len;
      var array = [];
      var k, i;
      for (i = 0; i < d; i++) {
        k = Math.floor(Math.random() * len);
        array.push(arr[k]);
        arr.splice(k, 1);
        len = arr.length;
      }
      for (i = 0; i < d; i++) {
        arr[i] = array[i];
      }
      return arr;
    }
  },
  computed: {
    answers() {
      let incorrectAnswers = this.results[this.questionCount][
        "incorrect_answers"
      ];
      let correctAnswer = this.results[this.questionCount]["correct_answer"];
      // return all answers, shuffled
      return this.shuffle(incorrectAnswers.concat(correctAnswer));
    }
  }
};

Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app {
  height: 100vh;
}

.container {
  flex: 1;
}

.questions .card-header {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

.questions .card-footer {
  padding-top: 0.7rem;
  padding-bottom: 0.7rem;
}

.answers li {
  cursor: pointer;
  display: block;
  padding: 7px 15px;
  margin-bottom: 5px;
  border-radius: 6px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  background: #fff;
}

.answers li:last-child {
  margin-bottom: 0;
}

.answers li:hover {
  background: #fafafa;
}

.pager {
  list-style-type: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: space-between;
}

.pager li > a {
  display: inline-block;
  padding: 5px 10px;
  text-align: center;
  width: 100px;
  background-color: #fff;
  border: 1px solid rgba(0, 0, 0, 0.1);
  border-radius: 999px;
  text-decoration: none !important;
  color: #fff;
}

.pager li > a.disabled {
  pointer-events: none;
  background-color: #9d9d9d !important;
}

.logo {
  width: 30px;
}

.nav-item {
  width: 100%;
}

.card {
  width: 100%;
}

@media (min-width: 768px) {
  .nav-item {
    width: auto;
  }

  .card {
    width: 67%;
  }
}

@media (min-width: 992px) {
  .card {
    width: 50%;
  }
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@next"></script>

<div id="quiz_app" class="container questions d-flex align-items-center justify-content-center my-3">
    <div v-if="results.length" class="card shadow-sm">
      <div class="card-header bg-light h6">
        {{results[questionCount]['question']}}
      </div>
      <div class="card-body">
        <ul class="answers list-unstyled m-0">

          <li v-for="answer in answers" @click="verifyAnswer(answer)" :class="{'text-white bg-success' : verifyAnswer(answer)}">{{answer}}</li>
        </ul>
      </div>
      <div class="card-footer bg-white">
        <ul class="pager">
          <li><a href="#" @click="prevQuestion" class="bg-dark" :class="{'disabled' : questionCount == 0}">Previous</a></li>
          <li class="d-flex align-items-center text-secondary font-weight-bold small">Question {{questionCount + 1}} of {{results.length}}</li>
          <li><a href="#" class="bg-dark" :class="{'disabled' : questionCount == results.length - 1}" @click="nextQuestion">Next</a></li>
        </ul>
      </div>
    </div>
  </div>

The Issue

Surprisingly, the verifyAnswer(answer) function gets called even before any click event occurs.

Query

What could be the mistake in my implementation?

Answer №1

UPDATED

checkAnswer() is triggered immediately when not within a handler function.

Perhaps this explanation will be useful: upon calling checkAnswer(), capture the selected answer as selectedAnswer and verify if it is correct using isCorrect. These two states are then used to compare the looped answers.

<li
  v-for="answer in answers"
  :key="answer"
  @click="checkAnswer(answer)"
  :class="{
    'text-white bg-success' : (selectedAnswer === answer && isCorrect),
    'text-white bg-danger' : (selectedAnswer === answer && !isCorrect)
  }"
>
  {{answer}}
</li>

data() {
    return {
        isCorrect: false,
        selectedAnswer: ''
        ...
    }
},
methods: {
  checkAnswer(answer) {
    // Verify if the chosen answer matches the correct answer
    this.selectedAnswer = answer

    if (answer == this.results[this.questionCount]["correct_answer"]) {
      this.isCorrect = true
    } else {
      this.isCorrect = false
    }
  },
}

https://jsfiddle.net/renzivan15/fw10q5og/12/

Answer №2

Prior to clicking, this code is being executed:

:class="{'text-white bg-success' : checkAnswer(answer)}"
.
It's important to store the state of each answer in a variable and modify it using the designated method. Additionally, utilizing :key for looped elements is highly recommended.

Answer №3

One issue arises when the code checkAnswer is called in the HTML template, causing it to execute immediately upon rendering. To address this, consider assigning the class based on a computed property rather than directly calling the function.

<li v-for="answer in answers" @click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can we expect Karma to receive updates for upcoming versions of Angular and Jasmine?

We recently attempted to upgrade our company's Angular module, which required updating dependencies as well. Upon upgrading to the latest versions, we encountered an issue with the Jasmine-karma-HTML-Reporter due to its reliance on Jasmine-core 4.x.x ...

I'm wondering how to adjust the index in a loop so that it starts at 1 instead of the standard 0

Currently, I am using a .each function to create a dropdown list. As we know, the index of an array element always begins at 0 in programming. However, for my specific requirement, I need the first value in the dropdown list to start at 1 instead of 0. ...

Uncertain about the process of accessing req.body data

I'm having trouble retrieving the body data on my server, even with simple .get requests. Despite simplifying it to just a basic call, I still can't seem to access the body data. This issue is hindering more complex calls that require accessing t ...

Coffeescript does not allow setting the AngularJS controller property as the last line of code

Having an issue while using Coffeescript to define a controller with the "HomeController as homeCtrl" syntax. angular.module('myApp.controllers',[]).controller("HomeController", -> @someArray = [] # return ) Encountering a problem ...

Cache of JavaScript file when the back button is pressed

I am in the process of developing a unique JavaScript widget that will showcase random ads generated by php inside an iframe. With each page refresh, a fresh batch of random advertisements is produced. If a user clicks on an ad, they are taken to the cor ...

Is it possible to utilize CommonJS, TypeScript, and React together without the need for bundling

Having attempted to utilize CommonJS, TypeScript, and React together, I encountered an issue with the initial code loading: Index.html <script type="text/javascript"> function loadScript(url) { var head = doc ...

The previous Http Get request is canceled by a new Http Get request

Currently, I am diving into the world of Ajax, JavaScript, and HTML while working on an application that triggers two consecutive "get" requests upon the user clicking a button. To simulate a coffee order being brewed, I use TimeUnit.SECONDS.sleep(10) in m ...

Filtering select options dynamically in Angular

Utilizing an ng-repeat to iterate through objects for display in Angular. Each object includes a select element with a property of the object. The goal is to filter options based on the selected value for each object's other properties. The challenge ...

Creating a variable that is named after an existing variable

Creating a new variable with a dynamic name can be tricky. Let's take a look at an example: function preloader(imglist) { var imgs = imglist.split("|"); for (i in imgs) { var img_{i} = new Image; img_{i}.src = imgs[i]; ...

Incorporating a Vue component with highcharts tooltip using vue-portal technology

I've made good progress, but I seem to be hitting a roadblock at this point. Here's an example of my work so far: https://codesandbox.io/s/highcharts-vue-demo-forked-5pgv2h?file=/src/components/Chart.vue My Approach I'm keeping my explan ...

Learn how to make a contenteditable div perform tab spacing with a simple keystroke of the tab key

I've been trying to figure out how to make this contenteditable div add tab spacing when I press the tab key, but so far no luck. If anyone has a solution for this issue, please feel free to share. You can use an ID, class, or any other method to get ...

How can I generate rotating images using jQuery or JavaScript similar to the one shown here?

http://www.example.com/ On a website similar to example.com, I noticed that when hovering over one of the topics listed, the image rotates. I am interested in creating this interactive effect using either jQuery or JavaScript. Is there a method to access ...

What is the proper way to utilize a class with conditional export within the Angular app.module?

This query marks the initiation of the narrative for those seeking a deeper understanding. In an attempt to incorporate this class into app.module: import { Injectable } from '@angular/core'; import { KeycloakService } from 'keycloak-angul ...

Angular's $http service fetches data successfully, however, it fails to update the scope with the

Within the realm of AngularJS, I've implemented a factory as follows: .factory('Users', function($http) { var users = []; return { getUsers: function() { return $http.get("data.json") .then(function(response) { ...

What is the method for binding 2 values with v-model?

While using the buefy <b-autocomplete> component, I encountered a situation where I needed to bind the Full Name into the input field. However, the data consists of list[index].first_name and list[index].last_name, with the index being from a v-for l ...

Adjusting an SVG image element in real-time based on information retrieved from a mySQL database

Seeking to dynamically alter an SVG element based on data retrieved from a MySQL database. Imagine an SVG illustration in Adobe Illustrator featuring a table (rectangle) with 4 chairs (circles) around it. The goal is to represent each chair as a 'pe ...

Embed Vue component within data section

I am attempting to create a custom tooltip for apexcharts similar to the example shown here: tooltip: { custom: function({series, seriesIndex, dataPointIndex, w}) { return '<div class="arrow_box">' + '<span> ...

Should you approach TypeScript modules or classes with a focus on unit testing?

When it comes to unit testing in TypeScript, which content architecture strategy is more effective: Creating modules or classes? Module Example: moduleX.method1(); // Exported method Class Example: var x = moduleX.method1(); // Public method ...

Synchronous: Establishing a connection to MongoDB once the server has successfully launched

I am curious to understand the interaction between Node.js asynchronous functions and MongoDB. If the server initializes before establishing a connection, and if my templates or Ajax requests rely on database data, will the serving of the HTML fail or will ...

Having trouble logging in with the script, even though I have already set up the correct username and password in my

After purchasing the "Jqcm - Premium Responsive Quiz Engine" script, the author has disappeared and has not been responding to any questions for months. When attempting to log in to the backend with the provided username and password, a circling bar appea ...