Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

James Walsh
James Walsh
7,377 Points

Validating input & answer

Having trouble validating the answer for the text input field (Q2) I have created the input dynamically via JS. In the showResults function i have a conditional statement to detect which input type the question is but i keep getting an error on the text input. The error reads: Cannot read property value of null.

Can anybody help me with this?

//Variables
//Saving HTML ID's as varibales for further use within JS.
const userName = document.getElementById('user-name'); //Selecting the HTML input by its ID.
const userBtn = document.getElementById('user-name-button'); //Selecting the submit button the user will press to submit their name
const userNameSection = document.getElementById('get-user-name-section') //Selecting the section the user name input and
//submit button is in. Selecting this as it will be displayed as none once a function is exectuted
const quizContainer = document.getElementById('quiz-container'); //Selecting the quiz container
const btnGroup = document.getElementById('button-group'); //Selecting the button group (next, previous & submit) as
//default CSS they are set to display none. In the getUserName function they will be set to display block so they will be visible

//Event Listeners
//Adding event listeners to elements so there is control on the actions once there is interaction between the elements and the users
userBtn.addEventListener('click', getUserName); //Adding an click event to the user button - Once the button is 'clicked' - Run the getUserName Function

function getUserName() {
    if (userName.value.length === 0) { //UserName.value is the input that is typed into the HTML field. If that value is equal to 0(empty)
        alert("Please enter your name!") //Alert the user to enter their name again
    } else {
        alert("Welcome " + userName.value + ". Good Luck!"); //A message with the users name displayed
        userNameSection.style.display = 'none'; //Hiding the section by setting its CSS display value to none
        btnGroup.style.display = 'block';
        quizContainer.style.display = "block"
    }
}


function buildQuiz() {
    //The output variable is a JS array. Once the questions loop runs, the questions will be taken from the myQuestions object and the asnwer fields will be dynamically created by JS and put into HTML. This will be done using the array method .push()
    const output = [];

    // for each question...
    myQuestions.forEach((currentQuestion, questionNumber) => {
        //******** NOTE: ADD LOOP HERE TO ADD QUESTION NUMBERS TO QUESTIONS **********
        // variable to store the list of possible answers
        const answers = [];

        if (currentQuestion.inputType === "radio") {
            // answers in a radio style ...
            // and for each available answer...
            for (letter in currentQuestion.answers) {
                // ...add an HTML radio button
                // add this question and its answers to the output
                answers.push (
                    `<label><input type="radio" name="question${questionNumber}" value="${letter}">
                    ${letter} : ${currentQuestion.answers[letter]}
                    </label>`);
            }

            // add this question and its answers to the output
            //Q:${questionNumber + 1} - This is writting the question number to the screen. +1 is used because array index starts at 0 - This applies to all of the below conditionals
            output.push(`<div class="slide"><div class="question"> Q:${questionNumber} ${currentQuestion.question} </div><div class="answers"> ${answers.join("")} </div></div>`);
        } else if (currentQuestion.inputType === "img") {
            // answers in a checkbox style ...
            for (letter in currentQuestion.answers) {
                // ...add an HTML checbox button
                answers.push(
                    `<label><div class="image-choice"><input type="radio" name="question"${questionNumber}" value="${letter}">
                     ${letter} : <img src="${currentQuestion.answers[letter]}" width="50" height="50"></div></label>`);
            }

            // add this question and its answers to the output
            output.push(`<div class="slide"><div class="question">  Q:${questionNumber} ${currentQuestion.question} </div><div class="answers"> ${answers.join("")} </div></div>`);
        } else if (currentQuestion.inputType === "text") {
            // answers in a text input style ...
            for (letter in currentQuestion.answers) {
                // add a HTML input text field
                //Adding a span element to wrap around the text input. Can now select this element in CSS to display block so the input field will be on its own line and give  give further styling
                //The ${letter} literal has been moved from this in the value because the text box should be blank from the start
                answers.push(`<span class="input-span"><label><input type="text" id="text-question" name="text-question${questionNumber}" value="${letter}"> : ${currentQuestion.answers[letter]}</label></span>`)
            }
            //Add this question and its answers to the output
            output.push(`<div class="slide"><div class="question"> Q:${questionNumber} ${currentQuestion.question} </div<div class="asnwers"> ${answers.join("")} </div></div>`)
        } 
    });

    // finally combine our output list into one string of HTML and put it on the page
    quizContainer.innerHTML = output.join('');
};



function showResults(){

    //Using querySelectorAll to select all the answers that are assigned a class and storing these answers in the answers container
    const answerContainers = quizContainer.querySelectorAll('.answers');
    //Starting off with the number of correct guesses set to 0. If a question is answered correctly, this variable will be overwritten with a new number
    let numCorrect = 0;
    //Using arrow function and looping through the questions array. There are two parameters in here, currentQuestion and questionNumber. 
    myQuestions.forEach( (currentQuestion, questionNumber) => {
    // find selected answer
      const answerContainer = answerContainers[questionNumber];
      //This is finding which radio button has been selected and storing it in a variable. This is how it is determined which if the answer is correct or not
      const selector = `input[name=question${questionNumber}]:checked`;
      const userAnswer = (answerContainer.querySelector(selector) || {}).value;

      // if answer is correct
      if(userAnswer === currentQuestion.correctAnswer){
        //If the answer is correct - add 1 to the numCorrect varibale
        numCorrect++;

        // color the answers green
        answerContainers[questionNumber].style.color = 'lightgreen';
      } else if (currentQuestion.inputType === "text") {
        const textselector = `input[name=text-question${questionNumber}]`;
        const userAnswer = (answerContainer.querySelector(textselector)).value;
            if (this.userAnswer === "enterprise") {
                numCorrect++;
            }
      }
      // if answer is wrong or blank
      else{
        // color the answers red
        answerContainers[questionNumber].style.color = 'red';
      }
    });

    // show number of correct answers out of total
    resultsContainer.innerHTML = `${numCorrect} out of ${myQuestions.length}`;
  }




function showSlide(n) {
    slides[currentSlide].classList.remove('active-slide');
    slides[n].classList.add('active-slide');
    currentSlide = n;
    if (currentSlide === 0) {
        previousButton.style.display = 'none';
    } else {
        previousButton.style.display = 'inline-block';
    }
    if (currentSlide === slides.length - 1) {
        nextButton.style.display = 'none';
        submitButton.style.display = 'inline-block';
    } else {
        nextButton.style.display = 'inline-block';
        submitButton.style.display = 'none';
    }
}

function showNextSlide() {
    showSlide(currentSlide + 1);
}

function showPreviousSlide() {
    showSlide(currentSlide - 1);
}


// Variables
//Const quizContainer = document.getElementById('quiz');
const resultsContainer = document.getElementById('results');
const submitButton = document.getElementById('submit');
const myQuestions = [{
        //Question 1
        question: "What is the name of Captain Sisko's son?",
        answers: {
            a: "Jake",
            b: "Wesley",
            c: "Myles"
        },
        correctAnswer: "a",
        //The a new inputType element to the object - This will be used in the build quiz function and will determine which input type the question will be able answerd in - 
        //As example if the inputType is equal to radio, this means the question will be a multiple choice question with a radio button as the answer input type
        //This rule applies to all questions
        inputType: "radio"
    },
    { //Question 2
        question: "Finsish this quote.... 'Space, the final fronteir. These are the voyages of the starship......'",
        answers: {
            a: "enterprise",
            // b: "",
            // c: ""
        },
        correctAnswer: "enterprise",
        inputType: "text"
    },
    { //Question 3
        question: "Who is the Captain of the USS Voyager?",
        answers: {
            a: "Captain Picard",
            b: "Captain Janeway",
            c: "Captain Kirk"
        },
        correctAnswer: "b",
        inputType: "radio"
    },
    { //Question 4
        question: "What quadrant of space did the USS Voyger get lost in?",
        answers: {
            a: "Aplha Quadrant",
            b: "Beta Quadrant",
            c: "Delta Quadrant"
        },
        correctAnswer: "c",
        inputType: "radio"
    },
    { //Question 5
        question: "Which flag belongs to the Romulan Empire?",
        answers: {
            a: "img/klingon.jpg",
            b: "img/romulan.jpg",
            c: "img/romulan.jpg"
        },
        correctAnswer: "b",
        inputType: "img",
    },
    {
        //Question 6
        question: "What planet is Quark, Rom and Nog from?",
        answers: {
            a: "Vulcan",
            b: "Romulus",
            c: "Ferenginar"
        },
        correctAnswer: "c",
        inputType: "radio"
    },
    { //Question 7
        question: "Which transporter room is Chief Myles O'Brien's favourite?",
        answers: {
            a: "Transporter Room 3",
            b: "Transporter Room 7",
            c: "Transport Room 5"
        },
        correctAnswer: "a",
        inputType: "radio"
    },
    {
        //Question 8
        question: "What is the name of the first ship that Jean Luc Picard captained?",
        answers: {
            a: "USS Enterprise",
            b: "USS Stargazer",
            c: "USS Defiant"
        },
        correctAnswer: "b",
        inputType: "radio"
    },
    {
        //Question 9
        question: "Who does Commander Worf marry in Deep Space 9?",
        answers: {
            a: "Ezri Dax",
            b: "Kera Nerys",
            c: "Jadzia Dax"
        },
        correctAnswer: "c",
        inputType: "radio"
    },
    {
        //Question 10
        question: "What is the name of The Borg Drone who joined Starfleet?",
        answers: {
            a: "Hugh",
            b: "Seven of Nine",
            c: "Icheb"
        },
        correctAnswer: "b",
        inputType: "radio"
    }
];

// Kick things off
buildQuiz();


// Pagination
const previousButton = document.getElementById("previous");
const nextButton = document.getElementById("next");
const slides = document.querySelectorAll(".slide");
let currentSlide = 0;

// Show the first slide
showSlide(currentSlide);

// Event listeners
submitButton.addEventListener('click', showResults);
previousButton.addEventListener("click", showPreviousSlide);
nextButton.addEventListener("click", showNextSlide);
Steven Parker
Steven Parker
231,153 Points

It takes all the code to replicate the issue, including the HTML and possibly CSS componenets A good way to share everything at once is to make a snapshot of your workspace and post the link to it here.

1 Answer

Steven Parker
Steven Parker
231,153 Points

I can't replicate the issue without the rest of the code, but I can tell you that the error "Cannot read property <something> of null." generally means that a variable that you expect to refer to an element didn't get set. One common cause is a typo in either the HTML or JavaScript.

For example, if the HTML contained "<p id="frist">test</p>" and the JavaScript had "document.querySelector("#first").style.color = "Blue";", you would get this error because the ID was not found.