Problem
I built a JavaScript quizzer as a project from javascriptissexy, I haven’t added the improvements that they suggested. But I wanted to ask if I am doing this right, to be honest I am not really confident to what I did as I am sure there is more efficient way to do the things required.
I wanted to avoid much of repetition and I want to know if I actually did or just made it worst because I think it’s important to know that before I proceed to more stuff?
var Allquestions = [{
question: "Who is naruto's father?",
choices: ["Jiraiya", "Sarutobi", "Orichimaru", "Minato"],
answer: 3
},
{
question: "Why did sasuke left konoha?",
choices: ["To avenge his clan by killing his brother", "To train and surpass naruto", "To join the akatsuki", "To cast tsukuyomi upon the world"],
answer: 0
},
{
question: "In shippuden series how did naruto found out about his father?",
choices: ["Jiraiya being restored by edo tensei", "Naruto almost ripping the 9 tails seal", "Naruto on brink of his death", "Pain telling him who's his father is"],
answer: 1
},
{
question: "Who founded the Akatsuki?",
choices: ["Nagato", "Pain", "Yahiko", "Obito/tobi"],
answer: 2
},
{
question: "When did the term 'Legendary Sannins' was coined?",
choices: ["During the First Shinobi World War", "During the Second Shinobi World War", "During the third Shinobi World War", "During the Fourth Great Ninja War"],
answer: 1
},
{
question: "Besides Might guy father Might duy, who else is dubbed as 'Eternal Genin'?",
choices: ["Naruto Uzumaki", "Kosuke Maruboshi", "Tobirama Senju", "Chōji Akimichi"],
answer: 1
},
{
question: "Was naruto able to control his jinjuriki during the pain attack?",
choices: ["Yes, his father helped him", "Yes, he trained at Mount Myōboku to control the 9 tails power", "No, he wasnt able to control the 9 tails power", "No, he wasnt able to control the 9 tails but he became friend with kurama"],
answer: 2
},
{
question: "Kakashi's father Sakumo Hatake is renowned across the shinobi world as Konoha's White Fang, what is Kakashis famous title?",
choices: ["Copy Ninja Kakashi", "Kakashi the Sharingan", "New White Fang", "Konoha's Yellowflash"],
answer: 1
}
];
// GLOBAL SCOPE //
var answersArr = []; // STORED ANSWERS //
var countPage = 1; // FAKE PAGE DEFAULT VALUE //
var alreadyAnswered = false; // UNIQUE ANSWER VALIDATION //
var page = 0; // INDEX PAGE DEFAULT VALUE //
var textNode = ""; // QUESTION TEXT DEFAULT VALUE //
var msgbox = "#info-msg";
var currentScore = "#info-score";
var score = 0; // COUNT CORRECT ANSWER //
var indexController = 0; // INDEX DEFAULT VALUE //
// READY AND CALL //
$(document).ready(function() {
$(msgbox).empty();
createQuiz(page); //CREATE QUESTION DEFAULT //
$("#navigate").on('click', function() {
validateAnswer(); //VALIDATE ANSWER BEFORE PROCEEDING//
});
$("#submit").on('click', function(){
checkAnswer(); //LET'S CHECK ANSWERS //
})
})
// FUNCTIONS //
function createQuiz(crPage) {
// CURRENT INDEX QUESTION //
textNode = Allquestions[indexController].question;
switch (crPage) {
case 0:
appendQuestion() //APPEND CURRENT QUESTION//
appendChoices() //APPEND CORRESPONDING CHOICES//
break;
case 1:
appendQuestion()
appendChoices()
break;
case 2:
appendQuestion()
appendChoices()
break;
case 3:
appendQuestion()
appendChoices()
break;
case 4:
appendQuestion()
appendChoices()
break;
case 5:
appendQuestion()
appendChoices()
break;
case 6:
appendQuestion()
appendChoices()
break;
case 7:
appendQuestion()
appendChoices()
default:
// code
}
}
function checkAnswer(){
var i;
var ar = answersArr.length;
for(i=0; i < ar; i++){
if(answersArr[i] === Allquestions[i].choices[Allquestions[i].answer]){ //CHECK IF ANSWER IS CORRECT//
score += 5; //ADD +5 ON EVERY CORRECT ANSWER//
}
}
$(currentScore).html("SCORE :" + score); // SHOW OUR SCORE//
answersArr = [];
}
function validateAnswer() {
var inputElem = $("input[name=answer]:checked"); //GET SELECTED ANSWER//
if (inputElem.val() !== undefined) { //PROCEED//
answersArr.push(inputElem.val()); //LETS PUSH THE ANSWER TO OUR ARRAY //
if (page >= 7) {
$("#submit").css("display","block"); // LAST PAGE, CHECK ANSWER //
$("#navigate").css("display","none"); //HIDE NEXT BTN//
}
else {
page += 1; // INCREMENT OUR PAGE COUNT //
indexController = page; //INDEX EQUALS TO CURRENT PAGE //
createQuiz(page); // UPDATE QUIZ //
updateDomPage(countPage); // LETS UPDATE PAGE COUNT //
}
}
else {
$(msgbox).html("You can't do that!").fadeIn(1000).fadeOut(200);//SHOW MESSAGE//
}
}
// UPDATE PAGE COUNT //
function updateDomPage(val) {
val = countPage += 1;
var pageHTML = $("#currentPage"); //GET ELEMENT THAT HOLD FAKE QUESTION COUNT//
pageHTML.html(val);
}
// APPEND QUESTION CHOICES BASED ON QUESTION INDEX //
function appendChoices() {
$("#choices").empty(); // REMOVE EXISTING CHOICES //
var i;
var al = Allquestions[indexController].choices.length;
for (var i = 0; i < al; i++) {
//console.log(Allquestions[indexController].choices[i]); // TEST ACCESS
$("#choices").append("<input class='radiobtn' type='radio' id='" + indexController + "' name='answer' value='" + Allquestions[indexController].choices[i] + "'>") //CREATE RADIO BUTTON WITH ID/VALUE BASE ON INDEX//
$("#choices").append("<label for='" + indexController + "'>" + Allquestions[indexController].choices[i] + "</label>") // CREATE LABEL CHOICES REPRESENTATION //
continue;
}
}
// APPEND QUESTIONS BASED ON INDEX //
function appendQuestion() {
$('legend').empty() // REMOVE EXISTING TEXTNODES //
.append(textNode); // APPEND CURRENT INDEX TEXTNODES //
}
*{
font-family: 'Gloria Hallelujah', cursive;
}
div.container{
max-width: 550px;
text-align: center;
margin: 0 auto;
}
.info{
background: #FF7739;
color: #ffffff;
text-align: center;
width: 100%;
height:30px;
}
div#info-msg{
display: none;
background: red;
clear:both;
}
#question-wrapper{
background: #083A85;
}
legend{
color: #ff7609;
}
label{
color: #AAA3AD;
}
<!DOCTYPE html>
<!-- HTML5 Hello world by kirupa - http://www.kirupa.com/html5/getting_your_feet_wet_html5_pg1.htm -->
<html lang="en-us">
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="./scripts/main.js"></script>
<link href="https://fonts.googleapis.com/css?family=Gloria+Hallelujah" rel="stylesheet">
<link rel="stylesheet" href="./styles/main.css" type="text/css" />
<title>Hello...</title>
</head>
<body>
<div class="container">
<div class="info">
<div id="info-score" style="float:left;font-size:13px;">
SCORE: 0
</div>
<div id="info-msg">
test message
</div>
</div>
<div class="quiz-box">
<div id="question-wrapper">
<fieldset>
<legend> </legend>
<div id="choices">
</div>
</fieldset>
</div>
</div>
<p><span id="currentPage">1</span> out of 8 questions</p>
</div>
<button id="navigate">Next</button>
<button id="submit" style="display:none">Submit</button>
</body>
</html>
Solution
Scoring
You always show the score on top of the questions – but you only calculate it once after all questions are answered.
From a UX point of view I would say you have two options:
Show the score all the time
In this case you should update it after each answer and include a feedback, so that it is clear why the score didn’t change:
Correct answer +5 points
Sorry, that’s not correct.
Show the score not until the final question
Just show the score after the user hits submit. In this case the user will still not know which answers were correct and which weren’t. So a summary would be nice.
Also the function should be called checkAnswers()
instead of checkAnswer()
. I was wondering why you run through all answers, when you – looking at the function’s name – try to check only one.
Cache DOM-elements
Instead of expensive re-querying of DOM-elements, you should get them once and store them in variables:
const buttonNext = $("#navigate");
buttonNext.click();
buttonNext.hide();
Redundant code
Take a look at createQuiz()
:
switch (crPage) {
case 0:
appendQuestion() //APPEND CURRENT QUESTION//
appendChoices() //APPEND CORRESPONDING CHOICES//
break;
case 1:
appendQuestion()
appendChoices()
break;
No matter what value crPage
holds, you call the exact same functions. So this could simply be:
function createQuiz(crPage) {
textNode = Allquestions[indexController].question;
appendQuestion();
appendChoices();
}
Changing CSS using JavaScript
Try to decouple your CSS and JavaScript as much as possible.
$("#submit").css("display","block");
Instead use a CSS class that you toggle:
buttonSubmit.addClass('is-active');
The main advantage is, that the JavaScript isn’t aware of the appearance of the element. Now you can easily change this:
.is-active {
display: block
}
to a fade in:
.is-active {
opacity: 1;
transition: all 0.5s;
}
HTML
To make you code valid, keep in mind, that form control elements like input|fieldset
must be inside a form
-element or must be referenced using the form
-attribute.
You could simply change this element:
<div id="question-wrapper"></div>
to
<form id="question-wrapper"></form>
Also a button
is a submit
-button by default. Spend them the type
-attribute and attach them to you form as well:
<button id="navigate" type="button" form="question-wrapper">Next</button>
CSS
Precedences can get tricky in large project. Try to avoid id-selector and don’t put an element-selector before an id-selector.
div#info-msg{}
Better use classes instead like:
.info-msg {}