Skip to content
Advertisement

‘undefined’ error prevents last question from displaying…works on previous ones though

I’ve modified a quiz app from some boilerplate code and I’m getting the dreaded ‘undefined’ error. I’ve debugged the error and found that when it gets to the last question in the index, no matter how many questions I create, the last question is displayed but the answer choices are not, although the previous answer choices were displayed.

I looked at several simular questions on SO including Javascript undefined error on last iteration and I have included return statements in the pertinent functions to no avail.

I just can’t wrap my head around why it will print every question and answer set prior the last one just fine, then get to the last question and only display the question with no answers?

Any help would be greatly appreciated. Thanks in advance.

pertinent code:

// script.js

const startButton = document.getElementById('start-btn')
const nextButton = document.getElementById('next-btn')
const questionContainerElement = document.getElementById('question-container')
const questionElement = document.getElementById('question')
const question = questionElement
const answerButtonsElement = document.getElementById('answer-buttons')
const questionImageElement = document.getElementById('question-image')
const hudItemClass = document.getElementsByClassName('hud-item')
const SCORE_POINTS = 4
const MAX_QUESTIONS = 6
const progressBarFull = document.getElementById('progressBarFull')
const progressText = document.getElementById('progressText')
const scoreText = document.getElementById('score')
let shuffledQuestions, currentQuestionIndex = 1, score = 0


startButton.addEventListener('click', startQuiz)
nextButton.addEventListener('click', () => {
    currentQuestionIndex++
    setNextQuestion()
})

function startQuiz() {
    console.log('Started')
    startButton.classList.add('hide')
    /* Shuffle the questions randomly, subtracting .5 to get a completely random
       number. 50% of the time the number will be negative and 50% positive. */
    shuffledQuestions = questions.sort(() => Math.random() - .5)
    questionContainerElement.classList.remove('hide')
    setNextQuestion()

}

function setNextQuestion() {
    resetState()
    console.log("Inside setNextQuestion prior to call to showQuestion: +" +
        "shuffledQuestions[currentQuestionIndex]: ", shuffledQuestions[currentQuestionIndex])
    showQuestion(shuffledQuestions[currentQuestionIndex])
    console.log("Inside setNextQuestion after call to showQuestion: +" +
        "shuffledQuestions[currentQuestionIndex]: ", shuffledQuestions[currentQuestionIndex])
    console.log("Inside setNextQuestion: currentQuestionIndex: ", currentQuestionIndex)
    return shuffledQuestions[currentQuestionIndex]

}

// Resets everything related to our form when we set a new question.
function resetState() {
    // Hide the 'Next' button after selecting an answer and showing the 'Next' button.
    nextButton.classList.add('hide')
    //hudItemClass.classList.add('hide')

    /*  If there is a child element inside the answerButtonsElement, remove it. This will
        remove the answer buttons from the previous question if any, resetting the
        answerButtonsElement.*/
    while (answerButtonsElement.firstChild) {
        answerButtonsElement.removeChild(answerButtonsElement.firstChild)
    }
}

function showQuestion(questions) {
    console.log("Inside showQuestion")
    progressText.innerText = `Question ${currentQuestionIndex} of ${MAX_QUESTIONS}`
    progressBarFull.classList.remove('hide')
    progressBarFull.style.width = `${(currentQuestionIndex / MAX_QUESTIONS) * 100}%`
    const img = document.createElement('img')
    img.src = "../images/code1.png"
    // Display the current shuffled question
    questionElement.innerText = questions.question //error here on the last question.
    // Insert an image at the appropriate question index.
    if (questions.id === 2) {
        questionImageElement.appendChild(img)
    } else {
        questionImageElement.innerText = " "
    }
    // For each answer in the questions array create a button element.
    questions.answers.forEach(answer => {
        const button = document.createElement('button')
        // Place the answer choice text on the button
        button.innerText = answer.text
        // Styles the button
        button.classList.add('btn')
        //answer.sort(Math.random() - 0.5)

        if (answer.correct) {
            button.dataset.correct = answer.correct
        }
        button.addEventListener('click', selectAnswer)
        answerButtonsElement.appendChild(button)
        console.log("End showQuestion")
    })
    return questions.question
}

function selectAnswer(e) {
    console.log("Inside selectAnswer")
    const selectedButton = e.target
    const correct = selectedButton.dataset.correct

    if (correct) {
        incrementScore(SCORE_POINTS)
    }
    answerButtonsElement.disabled = true;

    /* Should the body class be set for correct or wrong?  This will determine which style
    to present. */
    setStatusClass(document.body, correct)
    /*loop through and select the classes for each button.
      Comment out code to prevent user from seeing correct answers. */
    Array.from(answerButtonsElement.children).forEach(button => {
        setStatusClass(button, button.dataset.correct)
    })
    /* If the number of questions left to answer is greater than the index of the
        current question, then diplay the 'Next' button by unhiding it. */
    console.log("shuffledQuestions.length inside selectAnswer: ", shuffledQuestions.length)
    console.log("currentQuestionIndex inside selectAnswer: ", currentQuestionIndex)
    if (shuffledQuestions.length > currentQuestionIndex) {
        nextButton.classList.remove('hide')
    } else {
        //startButton.innerText = 'Restart'
        startButton.classList.remove('hide')
    }
}


/* This function takes an element and whether or not
   it's correct as arguments. If disabled, the user won't be aware of the
   correct answer immediately. */
function setStatusClass(element, correct) {
    // Clear any status it already has
    clearStatusClass(element)
    if (correct) {
        element.classList.add('correct') // add the 'correct' class with green color.

    } else {
        element.classList.add('wrong') // add the 'wrong' class with red color.
    }
    element.removeEventListener('click', selectAnswer)
}

// Clears the class status of the element if it has been set with 'setStatusClass.'
function clearStatusClass(element) {
    element.classList.remove('correct')
    element.classList.remove('wrong')

}

function incrementScore(num) {
    score += num
    scoreText.innerText = score
    // If the quiz has completed, display the 'end.html page.
    if ((shuffledQuestions.length) === 0 || currentQuestionIndex >= MAX_QUESTIONS) {
        console.log("shuffledQuestions.length: ", shuffledQuestions.length)
        console.log("currentQuestionIndex:", currentQuestionIndex)
        console.log("MAX_QUESTIONS: ", MAX_QUESTIONS)
        localStorage.setItem('mostRecentScore', score)
        return window.location.assign('/end.html')
    }
    // const questionsIndex = Math.floor(Math.random() * availableQuestions.length)
    // Remove or replace existing questions at the current question index.
    //availableQuestions.splice(questionsIndex, 1)
}

const questions = [
    {
        id: 1,
        question: ' 1. How many possible values are there for a boolean variable?',
        answers: [
            {text: '1', correct: false},
            {text: '2', correct: true},
            {text: '3', correct: false},
            {text: 'There is an infinite number of possibilities', correct: false}

        ]
    },
    {
        id: 2, // image = code1.png
        question: '2. What does this Python expression evaluate to?',
        answers: [

            {text: 'True', correct: false},
            {text: 'False', correct: false},
            {text: 'type<str>', correct: true},
            {text: '<str>type', correct: false}

        ]
    },
    {
        id: 3,
        question: '3. What is the purpose of a function?',
        answers: [
            {text: 'To provide a way to call code', correct: false},
            {text: 'To lessen the impact of variable binding', correct: false},
            {text: 'To provide concise code that can be called from anywhere in the program', correct: true},
            {text: 'To allow for easy access to variables and parameters', correct: false}

        ]
    },

    {
        id: 4,
        question: '4. Which Python code segment will display "Hello, world!" on the screen??',
        answers: [
            {text: 'display Hello, world!', correct: false},
            {text: `print("Hello, world!")`, correct: true},
            {text: `print "Hello, world!"`, correct: false},
            {text: `"Hello, world!"`, correct: false}

        ]
    },

    {
        id: 5,
        question: ' 5. Which is the most correct answer: What is the difference between an argument and a parameter?',
        answers: [
            {
                text: 'An argument is attached to a function and a parameter is attached to a function ' +
                    'call', correct: false
            },
            {
                text: 'An argument is attached to a function call and a parameter is associated with a function ' +
                    'definition', correct: true
            },
            {text: 'Parameters and arguments are interchangeable terms and can mean the same thing', correct: false},
            {text: 'Arguments and parameters are only necessary when functions are long.', correct: false}

        ]
    },

    {
        id: 6,
        question: ' 6. Which is the difference between a while loop and a for loop?',
        answers: [
            {text: 'A while loop is boolean structure and a for loop is not.', correct: false},
            {text: 'A while loop and a for loop are interchangeable.', correct: false},
            {text: 'A while loop iterates as long as a certain boolean condition exists.', correct: true},
            {text: 'A for loop is used when you don't know how many iterations are needed.', correct: false}

        ]
    }

]

Advertisement

Answer

Array is 0 indexed, meaning it starts at 0.

questionElement.innerText = questions.question

this above line inside of showQuestion gives you an error because at this point questions is null because of this

currentQuestionIndex = 1

you started your array at 1 and skipped the first question, by the time you get to the last question, showQuestion(shuffledQuestions[currentQuestionIndex]) would be showQuestion(shuffledQuestions[ 6 ]) and you only have 6 questions so at the 6th position is the 7th question(because at position 0 is your first question) which does not exist and returns a null error.

This is all I can gather from your js and in the future you should really post with working code, a simplified example to reproduce the issue so people can work on it

Try this

currentQuestionIndex = 0;

Update other parts of the code where you match ID

I have created an extremely simplified example of your code. only 3 questions and I call setNextQuestion() 3 times which should show each question once. The index is set at 1 and you can see the error that is produced. If you change the index to start at 0, it will work and show all 3 questions

const question = document.getElementById('question')
const SCORE_POINTS = 4
const MAX_QUESTIONS = 3
const answerButtonsElement = document.getElementById('answer-buttons')
const progressText = document.getElementById('progressText')
const scoreText = document.getElementById('score')
let shuffledQuestions, currentQuestionIndex = 1,
  score = 0
const questions = [{
    id: 1,
    question: ' 1. How many possible values are there for a boolean variable?',
    answers: [{
        text: '1',
        correct: false
      },
      {
        text: '2',
        correct: true
      },
      {
        text: '3',
        correct: false
      },
      {
        text: 'There is an infinite number of possibilities',
        correct: false
      }

    ]
  },
  {
    id: 2, // image = code1.png
    question: '2. What does this Python expression evaluate to?',
    answers: [

      {
        text: 'True',
        correct: false
      },
      {
        text: 'False',
        correct: false
      },
      {
        text: 'type<str>',
        correct: true
      },
      {
        text: '<str>type',
        correct: false
      }

    ]
  },
  {
    id: 3,
    question: '3. What is the purpose of a function?',
    answers: [{
        text: 'To provide a way to call code',
        correct: false
      },
      {
        text: 'To lessen the impact of variable binding',
        correct: false
      },
      {
        text: 'To provide concise code that can be called from anywhere in the program',
        correct: true
      },
      {
        text: 'To allow for easy access to variables and parameters',
        correct: false
      }
    ]
  }

];

function startQuiz() {
  setNextQuestion()
}

function setNextQuestion() {
  showQuestion(questions[currentQuestionIndex])
  currentQuestionIndex++;
}

// Resets everything related to our form when we set a new question.
function resetState() {}

function showQuestion(questions) {

  progressText.innerText = `Question ${currentQuestionIndex} of ${MAX_QUESTIONS}`;

  question.innerText = question.innerText + "n" + questions.question //error here on the last question.

  const button = document.createElement('button')

  button.innerText = questions.answers[0].text

  answerButtonsElement.appendChild(button)
}


setNextQuestion();
setNextQuestion();
setNextQuestion();
<div id="progressText"></div>
<div id="question"></div>

<div id="answer-buttons"></div>
<div id="score"></div>
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement