1

I am going through this JavaScript tutorial and ran into an issue that I hope someone can assist me with. After selecting the last question on the quiz, my showScore() function displays the results as "undefined". Through some further debugging, I found that it was a problem with my quiz object. In my PopulateQuestion() function, I am able to print out the quiz object before executing the showScore() function. However, when I attempt to print out the quiz object from within the showScore() function, it returns undefined.

I would like to work on my ability to debug issues that come up like this. Based on debugging that I have done so far, my educated guess is that this is a scope issue, but I am stuck. Does anyone have any suggestions for debugging this further?

Here is my code

Index.html

 <!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>JS Quiz</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="main.css">
  </head>
  <body>
    <div class="quiz-container">
      <div id="quiz">
        <h1>Star Wars Quiz</h1>
        <hr style="margin-top: 20px;" />
        <p id="question">Who is Darth Vader?</p>
        <div class="buttons">
          <button id="b0"><span id="c0"></span></button>
          <button id="b1"><span id="c1"></span></button>
          <button id="b2"><span id="c2"></span></button>
          <button id="b3"><span id="c3"></span></button>
        </div>
        <hr style="margin-top: 50px" />
        <footer>
          <p id="progress">Question x of n</p>
        </footer>
      </div>
    </div>
    <script src="quiz-controller.js"></script>
    <script src="question.js"></script>
    <script src="app.js"></script>
  </body>
</html>

app.js

function populateQuestion() {
  if(quiz.isEnded()) {
    // display score
    console.log(quiz);
    showScore();
  } else {
    // display question
    var qElement = document.getElementById('question');
    qElement.innerHTML = quiz.getCurrentQuestion().text;

    // display choices
    var choices = quiz.getCurrentQuestion().choices;
    for(var i = 0; i < choices.length; i++) {
      var choice = document.getElementById('c' + i);
      choice.innerHTML = choices[i];
      guess("b" + i, choices[i]);
    }
    showProgress();
  }
}

function guess(id, guess) {
    var button = document.getElementById(id);
    button.onclick = function() {
      quiz.guess(guess);
      populateQuestion();
    };
}

function showProgress() {
    var currentQuestionNum = quiz.questionIndex + 1;
    var progress = document.getElementById("progress");
    progress.innerHTML = "Question " + currentQuestionNum + " of " + quiz.questions.length;
}

function showScore() {
  console.log(quiz);
  var resultsHTML = "<h1>Results</h1>";
  resultsHTML += "<h2 id='score'>Your Score: " + quiz.getScore() + "</h2>";
  var quiz = document.getElementById("quiz");
  quiz.innerHTML = resultsHTML;
}

var questions = [
  new Question("Who is Darth Vader?",
  ["Luke Skywalker", "Anakin Skywalker", "Your Mom", "Your Dad"],
  "Anakin Skywalker"),
  new Question("What is the name of the third episode?",
  ["Return of the Jedi", "Revenge of the Sith", "A New Hope", "The Empire Strikes Back"],
  "Revenge of the Sith"),
  new Question("Who is Anakin Skywalker's son?",
  ["Luke Skywalker", "Anakin Skywalker", "Your Mom", "Your Dad"],
  "Luke Skywalker"),
  new Question("What is the name of the sixth episode?",
  ["Return of the Jedi", "Revenge of the Sith", "A New Hope", "The Empire Strikes Back"],
  "Return of the Jedi")
];

var quiz = new Quiz(questions);
populateQuestion();

question.js

function Question(text, choices, answer) {
  this.text = text;
  this.choices = choices;
  this.answer = answer;
}

Question.prototype.correctAnswer = function(choice) {
  return choice === this.answer;
};

quiz-controller.js

function Quiz(questions) {
  this.score = 0;
  this.questionIndex = 0;
  this.questions = questions;
}

Quiz.prototype.getScore = function() {
    return this.score;
};

Quiz.prototype.getCurrentQuestion = function() {
  return this.questions[this.questionIndex];
};

Quiz.prototype.isEnded = function() {
  return this.questionIndex === this.questions.length;
};

Quiz.prototype.guess = function(answer) {
  if(this.getCurrentQuestion().correctAnswer(answer)) {
    this.score++;
  }

  this.questionIndex++;
};

2 Answers 2

2

Your problem is that in the showScore() function you define a local variable with the name quiz. This local variable hides the global variable with the same name (even though it is defined later in the code).

You can easily fix that by renaming your local variable in showScore (below shown as q instead of quiz):

function populateQuestion() {
  if(quiz.isEnded()) {
    // display score
    console.log(quiz);
    showScore();
  } else {
    // display question
    var qElement = document.getElementById('question');
    qElement.innerHTML = quiz.getCurrentQuestion().text;

    // display choices
    var choices = quiz.getCurrentQuestion().choices;
    for(var i = 0; i < choices.length; i++) {
      var choice = document.getElementById('c' + i);
      choice.innerHTML = choices[i];
      guess("b" + i, choices[i]);
    }
    showProgress();
  }
}

function guess(id, guess) {
    var button = document.getElementById(id);
    button.onclick = function() {
      quiz.guess(guess);
      populateQuestion();
    };
}

function showProgress() {
    var currentQuestionNum = quiz.questionIndex + 1;
    var progress = document.getElementById("progress");
    progress.innerHTML = "Question " + currentQuestionNum + " of " + quiz.questions.length;
}

function showScore() {
  console.log(quiz);
  var resultsHTML = "<h1>Results</h1>";
  resultsHTML += "<h2 id='score'>Your Score: " + quiz.getScore() + "</h2>";
  var q = document.getElementById("quiz");
  q.innerHTML = resultsHTML;
}

var questions = [
  new Question("Who is Darth Vader?",
  ["Luke Skywalker", "Anakin Skywalker", "Your Mom", "Your Dad"],
  "Anakin Skywalker"),
  new Question("What is the name of the third episode?",
  ["Return of the Jedi", "Revenge of the Sith", "A New Hope", "The Empire Strikes Back"],
  "Revenge of the Sith"),
  new Question("Who is Anakin Skywalker's son?",
  ["Luke Skywalker", "Anakin Skywalker", "Your Mom", "Your Dad"],
  "Luke Skywalker"),
  new Question("What is the name of the sixth episode?",
  ["Return of the Jedi", "Revenge of the Sith", "A New Hope", "The Empire Strikes Back"],
  "Return of the Jedi")
];

function Question(text, choices, answer) {
  this.text = text;
  this.choices = choices;
  this.answer = answer;
}

Question.prototype.correctAnswer = function(choice) {
  return choice === this.answer;
};

function Quiz(questions) {
  this.score = 0;
  this.questionIndex = 0;
  this.questions = questions;
}

Quiz.prototype.getScore = function() {
    return this.score;
};

Quiz.prototype.getCurrentQuestion = function() {
  return this.questions[this.questionIndex];
};

Quiz.prototype.isEnded = function() {
  return this.questionIndex === this.questions.length;
};

Quiz.prototype.guess = function(answer) {
  if(this.getCurrentQuestion().correctAnswer(answer)) {
    this.score++;
  }

  this.questionIndex++;
};

var quiz = new Quiz(questions);
populateQuestion();
 <!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>JS Quiz</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="main.css">
  </head>
  <body>
    <div class="quiz-container">
      <div id="quiz">
        <h1>Star Wars Quiz</h1>
        <hr style="margin-top: 20px;" />
        <p id="question">Who is Darth Vader?</p>
        <div class="buttons">
          <button id="b0"><span id="c0"></span></button>
          <button id="b1"><span id="c1"></span></button>
          <button id="b2"><span id="c2"></span></button>
          <button id="b3"><span id="c3"></span></button>
        </div>
        <hr style="margin-top: 50px" />
        <footer>
          <p id="progress">Question x of n</p>
        </footer>
      </div>
    </div>
    <script src="quiz-controller.js"></script>
    <script src="question.js"></script>
    <script src="app.js"></script>
  </body>
</html>

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Fridjon! I see where I made my mistake now. I fixed it and it worked.
0

There is private variable quiz in showScore function which is getting hoisted to the top of the function as follows:

Your code:
function showScore() {
    console.log(quiz);
    var resultsHTML = "<h1>Results</h1>";
    resultsHTML += "<h2 id='score'>Your Score: " + quiz.getScore() + "</h2>";
    var quiz = document.getElementById("quiz");

    What internally happens:
        function showScore() {
            var quiz = undefined; // hoisting is happening here. So quiz is not reffering to public quiz variable anymore.
            console.log(quiz);
            var resultsHTML = "<h1>Results</h1>";
            resultsHTML += "<h2 id='score'>Your Score: " + quiz.getScore() + "</h2>";
            var quiz = document.getElementById("quiz");

Comments

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.