In this article, we will Build a Quiz App With JavaScript. We will also use HTML, CSS for making Skeleton and designing purpose. This will be a simple quiz application where we will have a button to start quiz, then after we will have information and rules about quiz, After that we will add questions, and lastly we will result page. This quiz also contains a timer, so if the timer ran out then the answer will be considered as wrong.
Pre-requisites to Build a Quiz App with JavaScript
- Good knowledge of HTML.
- Good knowledge of CSS & CSS3.
- Good knowledge of JavaScript.
Creating HTML Markup & Setting up Values
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Awesome Quiz App | CodingNepal</title>
<link rel="stylesheet" href="style.css">
<!-- FontAweome CDN Link for Icons-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"/>
</head>
<body>
<!-- start Quiz button -->
<div class="start_btn"><button>Start Quiz</button></div>
</body>
</html>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
background: #007bff;
}
::selection{
color: #fff;
background: #007bff;
}
.start_btn,
{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2),
0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.start_btn button{
font-size: 25px;
font-weight: 500;
color: #007bff;
padding: 15px 30px;
outline: none;
border: none;
border-radius: 5px;
background: #fff;
cursor: pointer;
}
First of all, In HTML we have added a button to start the quiz, now in CSS, we will customize the button and also background. We have added font-awesome for icons and Poppins font, so our content looks good it is an optional thing, but it is good. Then after we have applied some basic CSS styling to style our button, we have used transform
property to centralize our button, we can also use justify-content
and align-items
to center but instead of 2 properties we have used 1.
Customizing Information Box
<div class="start_btn"><button>Start Quiz</button></div>
<!-- Info Box -->
<div class="info_box">
<div class="info-title"><span>Some Rules of this Quiz</span></div>
<div class="info-list">
<div class="info">1. You will have only <span>15 seconds</span> per each question.</div>
<div class="info">2. Once you select your answer, it can't be undone.</div>
<div class="info">3. You can't select any option once time goes off.</div>
<div class="info">4. You can't exit from the Quiz while you're playing.</div>
<div class="info">5. You'll get points on the basis of your correct answers.</div>
</div>
<div class="buttons">
<button class="quit">Exit Quiz</button>
<button class="restart">Continue</button>
</div>
</div>
.start_btn,
.info_box,
{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2),
0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.info_box.activeInfo,
.quiz_box.activeQuiz,
.result_box.activeResult{
opacity: 1;
z-index: 5;
pointer-events: auto;
transform: translate(-50%, -50%) scale(1);
}
.info_box{
width: 540px;
background: #fff;
border-radius: 5px;
transform: translate(-50%, -50%) scale(0.9);
opacity: 0;
pointer-events: none;
transition: all 0.3s ease;
}
.info_box .info-title{
height: 60px;
width: 100%;
border-bottom: 1px solid lightgrey;
display: flex;
align-items: center;
padding: 0 30px;
border-radius: 5px 5px 0 0;
font-size: 20px;
font-weight: 600;
}
.info_box .info-list{
padding: 15px 30px;
}
.info_box .info-list .info{
margin: 5px 0;
font-size: 17px;
}
.info_box .info-list .info span{
font-weight: 600;
color: #007bff;
}
.info_box .buttons{
height: 60px;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 30px;
border-top: 1px solid lightgrey;
}
.info_box .buttons button{
margin: 0 5px;
height: 40px;
width: 100px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
border: none;
outline: none;
border-radius: 5px;
border: 1px solid #007bff;
transition: all 0.3s ease;
}
.buttons button.restart{
color: #fff;
background: #007bff;
}
.buttons button.restart:hover{
background: #0263ca;
}
.buttons button.quit{
color: #007bff;
background: #fff;
}
.buttons button.quit:hover{
color: #fff;
background: #007bff;
}
As we have created our first page now lets move on to information content, for info content we have written some rules, and we have 2 buttons for continue and exit quiz. Now for CSS part, we have just centered our info part to center, and we have used some styling on the page. Also, we have styled our buttons and added some hovering effects on them, we also used transition effect to smoother the effects. Okay, so this info content will show above the start button, so we have used info_box opacity to 0 and to show this box we have added opacity to 1 on active_info class, we will access this class with the help of JavaScript later.
Creating Quiz Box
<div class="quiz_box">
<header>
<div class="title">Awesome Quiz Application</div>
<div class="timer">
<div class="time_left_txt">Time Left</div>
<div class="timer_sec">15</div>
</div>
<div class="time_line"></div>
</header>
<section>
<div class="que_text">
<span>What does HTML stand for?</span>
</div>
<div class="option_list">
<div class="option">
<span>Hyper Text Preprocessor</span>
<div class="icon cross"><i class = "fas fa-times"></i></div>
</div>
<div class="option">
<span>Hyper Text Markup Language</span>
<div class="icon tick"><i class = "fas fa-check"></i></div>
</div>
<div class="option">
<span>Hyper Text Multiple Language</span>
</div>
<div class="option">
<span>Hyper Tool Multi Language</span>
</div>
</div>
</section>
<!-- footer of Quiz Box -->
<footer>
<div class="total_que">
<span><p>2</p> of <p>5</p> Questions</span>
</div>
<button class="next_btn">Next Que</button>
</footer>
</div>
.quiz_box{
width: 550px;
background: #fff;
border-radius: 5px;
transform: translate(-50%, -50%) scale(0.9);
opacity: 1;
pointer-events: none;
transition: all 0.3s ease;
}
.quiz_box header{
position: relative;
z-index: 2;
height: 70px;
padding: 0 30px;
background: #fff;
border-radius: 5px 5px 0 0;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0px 3px 5px 1px rgba(0,0,0,0.1);
}
.quiz_box header .title{
font-size: 20px;
font-weight: 600;
}
.quiz_box header .timer{
color: #004085;
background: #cce5ff;
border: 1px solid #b8daff;
height: 45px;
padding: 0 8px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
width: 145px;
}
.quiz_box header .timer .time_left_txt{
font-weight: 400;
font-size: 17px;
user-select: none;
}
.quiz_box header .timer .timer_sec{
font-size: 18px;
font-weight: 500;
height: 30px;
width: 45px;
color: #fff;
border-radius: 5px;
line-height: 30px;
text-align: center;
background: #343a40;
border: 1px solid #343a40;
user-select: none;
}
.quiz_box header .time_line{
position: absolute;
bottom: 0px;
left: 0px;
height: 3px;
background: #007bff;
}
.quiz_box section{
padding: 25px 30px 20px 30px;
background: #fff;
}
.quiz_box section .que_text{
font-size: 25px;
font-weight: 600;
}
.quiz_box section .option_list{
padding: 20px 0px;
display: block;
}
section .option_list .option{
background: aliceblue;
border: 1px solid #84c5fe;
border-radius: 5px;
padding: 8px 15px;
font-size: 17px;
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: space-between;
}
section .option_list .option:last-child{
margin-bottom: 0px;
}
section .option_list .option:hover{
color: #004085;
background: #cce5ff;
border: 1px solid #b8daff;
}
section .option_list .option.correct{
color: #155724;
background: #d4edda;
border: 1px solid #c3e6cb;
}
section .option_list .option.incorrect{
color: #721c24;
background: #f8d7da;
border: 1px solid #f5c6cb;
}
section .option_list .option.disabled{
pointer-events: none;
}
section .option_list .option .icon{
height: 26px;
width: 26px;
border: 2px solid transparent;
border-radius: 50%;
text-align: center;
font-size: 13px;
pointer-events: none;
transition: all 0.3s ease;
line-height: 24px;
}
.option_list .option .icon.tick{
color: #23903c;
border-color: #23903c;
background: #d4edda;
}
.option_list .option .icon.cross{
color: #a42834;
background: #f8d7da;
border-color: #a42834;
}
footer{
height: 60px;
padding: 0 30px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid lightgrey;
}
footer .total_que span{
display: flex;
user-select: none;
}
footer .total_que span p{
font-weight: 500;
padding: 0 5px;
}
footer .total_que span p:first-child{
padding-left: 0px;
}
footer button{
height: 40px;
padding: 0 13px;
font-size: 18px;
font-weight: 400;
cursor: pointer;
border: none;
outline: none;
color: #fff;
border-radius: 5px;
background: #007bff;
border: 1px solid #007bff;
line-height: 10px;
opacity: 1;
pointer-events: none;
transform: scale(0.95);
transition: all 0.3s ease;
}
footer button:hover{
background: #0263ca;
}
footer button.show{
opacity: 1;
pointer-events: auto;
transform: scale(1);
}
Now we need to add our quiz box, as we can see in our HTML part we have just hard coded our question, and footer part. But don’t worry about that, we just need it as the temporary code, so we can perform CSS styling our them. We will add the questions with the help of our JavaScript later.
Now here we have a very simple UI for quiz box where we have a title, timer, question and multiple choices, and a button. We have performed some styling on these elements, also we have added some little bit animations like hover effect etc. Note that, we have added opacity to 1 here on every element, but we need to turn it into 0 in order to work with JavaScript. We will show up this page using JavaScript.
Adding Result Box
<div class="result_box">
<div class="icon">
<i class="fas fa-crown"></i>
</div>
<div class="complete_text">You've completed the Quiz!</div>
<div class="score_text">
<span>and sorry, You got only <p>2</p> out of <p>5</p></span>
</div>
<div class="buttons">
<button class="restart">Replay Quiz</button>
<button class="quit">Quit Quiz</button>
</div>
</div>
.result_box{
background: #fff;
border-radius: 5px;
display: flex;
padding: 25px 30px;
width: 450px;
align-items: center;
flex-direction: column;
justify-content: center;
transform: translate(-50%, -50%) scale(0.9);
opacity: 1;
pointer-events: none;
transition: all 0.3s ease;
}
.result_box .icon{
font-size: 100px;
color: #007bff;
margin-bottom: 10px;
}
.result_box .complete_text{
font-size: 20px;
font-weight: 500;
}
.result_box .score_text span{
display: flex;
margin: 10px 0;
font-size: 18px;
font-weight: 500;
}
.result_box .score_text span p{
padding: 0 4px;
font-weight: 600;
}
.result_box .buttons{
display: flex;
margin: 20px 0;
}
.result_box .buttons button{
margin: 0 10px;
height: 45px;
padding: 0 20px;
font-size: 18px;
font-weight: 500;
cursor: pointer;
border: none;
outline: none;
border-radius: 5px;
border: 1px solid #007bff;
transition: all 0.3s ease;
}
.buttons button.restart{
color: #fff;
background: #007bff;
}
.buttons button.restart:hover{
background: #0263ca;
}
.buttons button.quit{
color: #007bff;
background: #fff;
}
.buttons button.quit:hover{
color: #fff;
background: #007bff;
}
Now, we have on our last content here which will be our result box to show score, Here again we hard coded our result content, but we will make it dynamic using JavaScript later. We have again applied some basic CSS styling, and again we have set its opacity to 1, but we will convert it to 0 after we complete our CSS part. We only added some colors and positioned into the center, also with some animations.
Adding Data to JavaScript File
let questions = [
{
numb: 1,
question: "What does HTML stand for?",
answer: "Hyper Text Markup Language",
options: [
"Hyper Text Preprocessor",
"Hyper Text Markup Language",
"Hyper Text Multiple Language",
"Hyper Tool Multi Language"
]
},
{
numb: 2,
question: "What does CSS stand for?",
answer: "Cascading Style Sheet",
options: [
"Common Style Sheet",
"Colorful Style Sheet",
"Computer Style Sheet",
"Cascading Style Sheet"
]
},
{
numb: 3,
question: "What does PHP stand for?",
answer: "Hypertext Preprocessor",
options: [
"Hypertext Preprocessor",
"Hypertext Programming",
"Hypertext Preprogramming",
"Hometext Preprocessor"
]
},
{
numb: 4,
question: "What does SQL stand for?",
answer: "Structured Query Language",
options: [
"Stylish Question Language",
"Stylesheet Query Language",
"Statement Question Language",
"Structured Query Language"
]
},
{
numb: 5,
question: "What does XML stand for?",
answer: "eXtensible Markup Language",
options: [
"eXtensible Markup Language",
"eXecutable Multiple Language",
"eXTra Multi-Program Language",
"eXamine Multiple Language"
]
},
]
First of all, In JS part, we will add our all questions and answers in the JS file here we have used questions.js file to store questions and answers. We have stored these data in an array, This looks like JSON format if you’re familiar with it of course. Now we don’t need hard coded data as we have done earlier to perform CSS.
Adding Functionality to Start Button
const start_btn = document.querySelector(".start_btn button");
const info_box = document.querySelector(".info_box");
start_btn.onclick = ()=>{
info_box.classList.add("activeInfo"); //show info box
}
Here, we can get access of HTML elements using querySelector
method, so we have here 2 elements are accessed, one for start button and other for info box. We’re just adding onClick
event on start button, here we are adding activeInfo class to info box. If you remember, we have added in the CSS file.
.info_box.activeInfo,
.quiz_box.activeQuiz,
.result_box.activeResult{
opacity: 1;
z-index: 5;
pointer-events: auto;
transform: translate(-50%, -50%) scale(1);
}
Adding Functionalities to Info_Box Buttons
const start_btn = document.querySelector(".start_btn button");
const info_box = document.querySelector(".info_box");
const exit_btn = info_box.querySelector(".buttons .quit");
const continue_btn = info_box.querySelector(".buttons .restart");
const quiz_box = document.querySelector(".quiz_box");
const result_box = document.querySelector(".result_box");
const option_list = document.querySelector(".option_list");
const time_line = document.querySelector("header .time_line");
const timeText = document.querySelector(".timer .time_left_txt");
const timeCount = document.querySelector(".timer .timer_sec");
// if exitQuiz button clicked
exit_btn.onclick = ()=>{
info_box.classList.remove("activeInfo"); //hide info box
}
// if continueQuiz button clicked
continue_btn.onclick = ()=>{
info_box.classList.remove("activeInfo"); //hide info box
quiz_box.classList.add("activeQuiz"); //show quiz box
showQuetions(0); //calling showQestions function
}
// getting questions and options from array
function showQuetions(index){
const que_text = document.querySelector(".que_text");
//creating a new span and div tag for question and option and passing the value using array index
let que_tag = '<span>'+ questions[index].numb + ". " + questions[index].question +'</span>';
let option_tag = '<div class="option"><span>'+ questions[index].options[0] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[1] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[2] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[3] +'</span></div>';
que_text.innerHTML = que_tag; //adding new span tag inside que_tag
option_list.innerHTML = option_tag; //adding new div tag inside option_tag
const option = option_list.querySelectorAll(".option");
}
}
Now, we need to add functionality to continue and exit button of info box. As we have added info box when start button gets clicked, so if the user hits the exit button then we will remove activeInfo
so we basically return to the start quiz content.
Now for continue button, as we have accessed all elements which we needed in whole JS code, so we will remove info box and add quiz box as we click on the continue button. After that, we have called a function for our question, which should appear as soon as we click the continue button. We have passed index of 0, which will bring the data of array which have index 0 in questions.js file.
Now we can remove our hard coded question, which we have done earlier. We have accessed here que_text element, then we have added new span for question using let que_tag = '<span>'+ questions[index].numb + ". " + questions[index].question +'</span>';
this line of code. Here we can get question using questions[index].question
.
Now for option, we did the same thing as we have done for getting question, we just need to use index to fetch every data, and finally, we added these variables to innerHTML
to show them in the box.
Adding Functionality to Next Button and Footer Part
let que_count = 0;
let que_numb = 1;
const next_btn = quiz_box.querySelector(".next_btn");
const bottom_ques_counter = document.querySelector("footer .total_que");
next_btn.onclick = ()=>{
if(que_count < questions.length - 1){ //if question count is less than total question length
que_count++; //increment the que_count value
que_numb++; //increment the que_numb value
showQuetions(que_count); //calling showQestions function
queCounter(que_numb); //passing que_numb value to queCounter
}
}
function queCounter(index){
//creating a new span tag and passing the question number and total question
let totalQueCounTag = '<span><p>'+ index +'</p> of <p>'+ questions.length +'</p> Questions</span>';
bottom_ques_counter.innerHTML = totalQueCounTag; //adding new span tag inside bottom_ques_counter
}
Now, we need to add functionality to our next button, and we want to show the question counter in the footer section. We have accessed our recommended classes and for button we added onClick
event listener. In this function, we are increasing our que_count
value and que_numb value when que_count
is less than length of questions.
And after that, we are calling our function showQuestions
to get the next question, and also we’re calling the function queCounter
with que_numb
argument. In this queCounter
function, we are adding span tag with dynamic number so each time question changes then queCounter
will call again and again. We will get question count using let totalQueCounTag = '<span><p>'+ index +'</p> of <p>'+ questions.length +'</p> Questions</span>';
and we are just showing them in the HTML using innerHTML
.
Adding Correct and Wrong Answer Functionality
function showQuetions(index){
const que_text = document.querySelector(".que_text");
//creating a new span and div tag for question and option and passing the value using array index
let que_tag = '<span>'+ questions[index].numb + ". " + questions[index].question +'</span>';
let option_tag = '<div class="option"><span>'+ questions[index].options[0] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[1] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[2] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[3] +'</span></div>';
que_text.innerHTML = que_tag; //adding new span tag inside que_tag
option_list.innerHTML = option_tag; //adding new div tag inside option_tag
const option = option_list.querySelectorAll(".option");
// set onclick attribute to all available options
for(i=0; i < option.length; i++){
option[i].setAttribute("onclick", "optionSelected(this)");
}
}
// creating the new div tags which for icons
let tickIconTag = '<div class="icon tick"><i class="fas fa-check"></i></div>';
let crossIconTag = '<div class="icon cross"><i class="fas fa-times"></i></div>';
//if user clicked on option
function optionSelected(answer){
let userAns = answer.textContent; //getting user selected option
let correcAns = questions[que_count].answer; //getting correct answer from array
const allOptions = option_list.children.length; //getting all option items
if(userAns == correcAns){ //if user selected option is equal to array's correct answer
userScore += 1; //upgrading score value with 1
answer.classList.add("correct"); //adding green color to correct selected option
answer.insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to correct selected option
console.log("Correct Answer");
console.log("Your correct answers = " + userScore);
}else{
answer.classList.add("incorrect"); //adding red color to correct selected option
answer.insertAdjacentHTML("beforeend", crossIconTag); //adding cross icon to correct selected option
console.log("Wrong Answer");
for(i=0; i < allOptions; i++){
if(option_list.children[i].textContent == correcAns){ //if there is an option which is matched to an array answer
option_list.children[i].setAttribute("class", "option correct"); //adding green color to matched option
option_list.children[i].insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to matched option
console.log("Auto selected correct answer.");
}
}
}
for(i=0; i < allOptions; i++){
option_list.children[i].classList.add("disabled"); //once user select an option then disabled all options
}
next_btn.classList.add("show"); //show the next button if user selected any option
}
Now, we need to add functionality for correct options and wrong options, So here we have added onClick on option in loop, and we are calling optionSelected
function. In this function, we are getting the user selected option with this let userAns = answer.textContent;
line of code, also, we are fetching the correct answer from the array using let correcAns = questions[que_count].answer;
. After that, we are checking the answer and user selected option, matching or not. If yes then we are adding the class correct which simply adds green color to the option, we have declared it in our CSS.
And if it’s false, then we will add an incorrect class which simply adds red color to the option. Apart from that, we need to add right and wrong ticks as well. Before doing that, we will remove our hard code from the HTML part, and we used let tickIconTag = '<div class="icon tick"><i class="fas fa-check"></i></div>';
, let crossIconTag = '<div class="icon cross"><i class="fas fa-times"></i></div>';
to correct tick and wrong tick correspondingly.
We can use answer.insertAdjacentHTML("beforeend", tickIconTag);
to show our Tick at the edge of the option, here “beforeend” is the parameter for insertAdjacentHTML
which show the tickIconTag
should be printed just before the end of the option. Similarly, we have done for cross icon.
Now we need to disable all the other options once we select any option then other options should be disabled, for that we have used option_list.children[i].classList.add("disabled");
code where option_list.children targets the options in which we have added class disabled so that these option will not be clickable.
Adding Timer and Result Box
const restart_quiz = result_box.querySelector(".buttons .restart");
const quit_quiz = result_box.querySelector(".buttons .quit");
// if restartQuiz button clicked
restart_quiz.onclick = ()=>{
quiz_box.classList.add("activeQuiz"); //show quiz box
result_box.classList.remove("activeResult"); //hide result box
timeValue = 15;
que_count = 0;
que_numb = 1;
userScore = 0;
widthValue = 0;
showQuetions(que_count); //calling showQestions function
queCounter(que_numb); //passing que_numb value to queCounter
clearInterval(counter); //clear counter
clearInterval(counterLine); //clear counterLine
startTimer(timeValue); //calling startTimer function
startTimerLine(widthValue); //calling startTimerLine function
timeText.textContent = "Time Left"; //change the text of timeText to Time Left
next_btn.classList.remove("show"); //hide the next button
}
// if quitQuiz button clicked
quit_quiz.onclick = ()=>{
window.location.reload(); //reload the current window
}
function showResult(){
info_box.classList.remove("activeInfo"); //hide info box
quiz_box.classList.remove("activeQuiz"); //hide quiz box
result_box.classList.add("activeResult"); //show result box
const scoreText = result_box.querySelector(".score_text");
if (userScore > 3){ // if user scored more than 3
//creating a new span tag and passing the user score number and total question number
let scoreTag = '<span>and congrats! 🎉, You got <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag; //adding new span tag inside score_Text
}
else if(userScore > 1){ // if user scored more than 1
let scoreTag = '<span>and nice 😎, You got <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag;
}
else{ // if user scored less than 1
let scoreTag = '<span>and sorry 😐, You got only <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag;
}
}
function startTimer(time){
counter = setInterval(timer, 1000);
function timer(){
timeCount.textContent = time; //changing the value of timeCount with time value
time--; //decrement the time value
if(time < 9){ //if timer is less than 9
let addZero = timeCount.textContent;
timeCount.textContent = "0" + addZero; //add a 0 before time value
}
if(time < 0){ //if timer is less than 0
clearInterval(counter); //clear counter
timeText.textContent = "Time Off"; //change the time text to time off
const allOptions = option_list.children.length; //getting all option items
let correcAns = questions[que_count].answer; //getting correct answer from array
for(i=0; i < allOptions; i++){
if(option_list.children[i].textContent == correcAns){ //if there is an option which is matched to an array answer
option_list.children[i].setAttribute("class", "option correct"); //adding green color to matched option
option_list.children[i].insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to matched option
console.log("Time Off: Auto selected correct answer.");
}
}
for(i=0; i < allOptions; i++){
option_list.children[i].classList.add("disabled"); //once user select an option then disabled all options
}
next_btn.classList.add("show"); //show the next button if user selected any option
}
}
}
function startTimerLine(time){
counterLine = setInterval(timer, 29);
function timer(){
time += 1; //upgrading time value with 1
time_line.style.width = time + "px"; //increasing width of time_line with px by time value
if(time > 549){ //if time value is greater than 549
clearInterval(counterLine); //clear counterLine
}
}
}
Now we are in our final stage where we need to add timer and timeline, For that we start with timer, for timer we have called a function name startTimer
basically, this function we need in our quiz_box and as we press next button then again we need to call in next button function as well.
Now, we have added a 1-second delay before the timer gets start, then we will get time value using timeCount.timeContext
, here have added 0 before the time value when time gets lower than 9, also if the time hits to 0 then we need to replace time with time off and after that, we will reveal answer, again we will copy and paste same logic as question, answer logic. We are increasing the timeline here using time_line.style.width = time + "px";
code.
Now for result box, we are just calling showQuestions
, queCounter
, and timer
functions. Now we have just added all the functions then after we will remove all the boxes and add the result box to show the result, we have just cleared timers and boxes.
Full Source Code of Build a Quiz App JavaScript
index.html
<!-- Created By CodingNepal - www.codingnepalweb.com -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Awesome Quiz App | CodingNepal</title>
<link rel="stylesheet" href="style.css">
<!-- FontAweome CDN Link for Icons-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"/>
</head>
<body>
<!-- start Quiz button -->
<div class="start_btn"><button>Start Quiz</button></div>
<!-- Info Box -->
<div class="info_box">
<div class="info-title"><span>Some Rules of this Quiz</span></div>
<div class="info-list">
<div class="info">1. You will have only <span>15 seconds</span> per each question.</div>
<div class="info">2. Once you select your answer, it can't be undone.</div>
<div class="info">3. You can't select any option once time goes off.</div>
<div class="info">4. You can't exit from the Quiz while you're playing.</div>
<div class="info">5. You'll get points on the basis of your correct answers.</div>
</div>
<div class="buttons">
<button class="quit">Exit Quiz</button>
<button class="restart">Continue</button>
</div>
</div>
<!-- Quiz Box -->
<div class="quiz_box">
<header>
<div class="title">Awesome Quiz Application</div>
<div class="timer">
<div class="time_left_txt">Time Left</div>
<div class="timer_sec">15</div>
</div>
<div class="time_line"></div>
</header>
<section>
<div class="que_text">
<!-- Here I've inserted question from JavaScript -->
</div>
<div class="option_list">
<!-- Here I've inserted options from JavaScript -->
</div>
</section>
<!-- footer of Quiz Box -->
<footer>
<div class="total_que">
<!-- Here I've inserted Question Count Number from JavaScript -->
</div>
<button class="next_btn">Next Que</button>
</footer>
</div>
<!-- Result Box -->
<div class="result_box">
<div class="icon">
<i class="fas fa-crown"></i>
</div>
<div class="complete_text">You've completed the Quiz!</div>
<div class="score_text">
<!-- Here I've inserted Score Result from JavaScript -->
</div>
<div class="buttons">
<button class="restart">Replay Quiz</button>
<button class="quit">Quit Quiz</button>
</div>
</div>
<!-- Inside this JavaScript file I've inserted Questions and Options only -->
<script src="js/questions.js"></script>
<!-- Inside this JavaScript file I've coded all Quiz Codes -->
<script src="js/script.js"></script>
</body>
</html>
style.css
/* importing google fonts */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
background: #007bff;
}
::selection{
color: #fff;
background: #007bff;
}
.start_btn,
.info_box,
.quiz_box,
.result_box{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2),
0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.info_box.activeInfo,
.quiz_box.activeQuiz,
.result_box.activeResult{
opacity: 1;
z-index: 5;
pointer-events: auto;
transform: translate(-50%, -50%) scale(1);
}
.start_btn button{
font-size: 25px;
font-weight: 500;
color: #007bff;
padding: 15px 30px;
outline: none;
border: none;
border-radius: 5px;
background: #fff;
cursor: pointer;
}
.info_box{
width: 540px;
background: #fff;
border-radius: 5px;
transform: translate(-50%, -50%) scale(0.9);
opacity: 0;
pointer-events: none;
transition: all 0.3s ease;
}
.info_box .info-title{
height: 60px;
width: 100%;
border-bottom: 1px solid lightgrey;
display: flex;
align-items: center;
padding: 0 30px;
border-radius: 5px 5px 0 0;
font-size: 20px;
font-weight: 600;
}
.info_box .info-list{
padding: 15px 30px;
}
.info_box .info-list .info{
margin: 5px 0;
font-size: 17px;
}
.info_box .info-list .info span{
font-weight: 600;
color: #007bff;
}
.info_box .buttons{
height: 60px;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 30px;
border-top: 1px solid lightgrey;
}
.info_box .buttons button{
margin: 0 5px;
height: 40px;
width: 100px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
border: none;
outline: none;
border-radius: 5px;
border: 1px solid #007bff;
transition: all 0.3s ease;
}
.quiz_box{
width: 550px;
background: #fff;
border-radius: 5px;
transform: translate(-50%, -50%) scale(0.9);
opacity: 0;
pointer-events: none;
transition: all 0.3s ease;
}
.quiz_box header{
position: relative;
z-index: 2;
height: 70px;
padding: 0 30px;
background: #fff;
border-radius: 5px 5px 0 0;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0px 3px 5px 1px rgba(0,0,0,0.1);
}
.quiz_box header .title{
font-size: 20px;
font-weight: 600;
}
.quiz_box header .timer{
color: #004085;
background: #cce5ff;
border: 1px solid #b8daff;
height: 45px;
padding: 0 8px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
width: 145px;
}
.quiz_box header .timer .time_left_txt{
font-weight: 400;
font-size: 17px;
user-select: none;
}
.quiz_box header .timer .timer_sec{
font-size: 18px;
font-weight: 500;
height: 30px;
width: 45px;
color: #fff;
border-radius: 5px;
line-height: 30px;
text-align: center;
background: #343a40;
border: 1px solid #343a40;
user-select: none;
}
.quiz_box header .time_line{
position: absolute;
bottom: 0px;
left: 0px;
height: 3px;
background: #007bff;
}
section{
padding: 25px 30px 20px 30px;
background: #fff;
}
section .que_text{
font-size: 25px;
font-weight: 600;
}
section .option_list{
padding: 20px 0px;
display: block;
}
section .option_list .option{
background: aliceblue;
border: 1px solid #84c5fe;
border-radius: 5px;
padding: 8px 15px;
font-size: 17px;
margin-bottom: 15px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: space-between;
}
section .option_list .option:last-child{
margin-bottom: 0px;
}
section .option_list .option:hover{
color: #004085;
background: #cce5ff;
border: 1px solid #b8daff;
}
section .option_list .option.correct{
color: #155724;
background: #d4edda;
border: 1px solid #c3e6cb;
}
section .option_list .option.incorrect{
color: #721c24;
background: #f8d7da;
border: 1px solid #f5c6cb;
}
section .option_list .option.disabled{
pointer-events: none;
}
section .option_list .option .icon{
height: 26px;
width: 26px;
border: 2px solid transparent;
border-radius: 50%;
text-align: center;
font-size: 13px;
pointer-events: none;
transition: all 0.3s ease;
line-height: 24px;
}
.option_list .option .icon.tick{
color: #23903c;
border-color: #23903c;
background: #d4edda;
}
.option_list .option .icon.cross{
color: #a42834;
background: #f8d7da;
border-color: #a42834;
}
footer{
height: 60px;
padding: 0 30px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid lightgrey;
}
footer .total_que span{
display: flex;
user-select: none;
}
footer .total_que span p{
font-weight: 500;
padding: 0 5px;
}
footer .total_que span p:first-child{
padding-left: 0px;
}
footer button{
height: 40px;
padding: 0 13px;
font-size: 18px;
font-weight: 400;
cursor: pointer;
border: none;
outline: none;
color: #fff;
border-radius: 5px;
background: #007bff;
border: 1px solid #007bff;
line-height: 10px;
opacity: 0;
pointer-events: none;
transform: scale(0.95);
transition: all 0.3s ease;
}
footer button:hover{
background: #0263ca;
}
footer button.show{
opacity: 1;
pointer-events: auto;
transform: scale(1);
}
.result_box{
background: #fff;
border-radius: 5px;
display: flex;
padding: 25px 30px;
width: 450px;
align-items: center;
flex-direction: column;
justify-content: center;
transform: translate(-50%, -50%) scale(0.9);
opacity: 0;
pointer-events: none;
transition: all 0.3s ease;
}
.result_box .icon{
font-size: 100px;
color: #007bff;
margin-bottom: 10px;
}
.result_box .complete_text{
font-size: 20px;
font-weight: 500;
}
.result_box .score_text span{
display: flex;
margin: 10px 0;
font-size: 18px;
font-weight: 500;
}
.result_box .score_text span p{
padding: 0 4px;
font-weight: 600;
}
.result_box .buttons{
display: flex;
margin: 20px 0;
}
.result_box .buttons button{
margin: 0 10px;
height: 45px;
padding: 0 20px;
font-size: 18px;
font-weight: 500;
cursor: pointer;
border: none;
outline: none;
border-radius: 5px;
border: 1px solid #007bff;
transition: all 0.3s ease;
}
.buttons button.restart{
color: #fff;
background: #007bff;
}
.buttons button.restart:hover{
background: #0263ca;
}
.buttons button.quit{
color: #007bff;
background: #fff;
}
.buttons button.quit:hover{
color: #fff;
background: #007bff;
}
script.js
//selecting all required elements
const start_btn = document.querySelector(".start_btn button");
const info_box = document.querySelector(".info_box");
const exit_btn = info_box.querySelector(".buttons .quit");
const continue_btn = info_box.querySelector(".buttons .restart");
const quiz_box = document.querySelector(".quiz_box");
const result_box = document.querySelector(".result_box");
const option_list = document.querySelector(".option_list");
const time_line = document.querySelector("header .time_line");
const timeText = document.querySelector(".timer .time_left_txt");
const timeCount = document.querySelector(".timer .timer_sec");
// if startQuiz button clicked
start_btn.onclick = ()=>{
info_box.classList.add("activeInfo"); //show info box
}
// if exitQuiz button clicked
exit_btn.onclick = ()=>{
info_box.classList.remove("activeInfo"); //hide info box
}
// if continueQuiz button clicked
continue_btn.onclick = ()=>{
info_box.classList.remove("activeInfo"); //hide info box
quiz_box.classList.add("activeQuiz"); //show quiz box
showQuetions(0); //calling showQestions function
queCounter(1); //passing 1 parameter to queCounter
startTimer(15); //calling startTimer function
startTimerLine(0); //calling startTimerLine function
}
let timeValue = 15;
let que_count = 0;
let que_numb = 1;
let userScore = 0;
let counter;
let counterLine;
let widthValue = 0;
const restart_quiz = result_box.querySelector(".buttons .restart");
const quit_quiz = result_box.querySelector(".buttons .quit");
// if restartQuiz button clicked
restart_quiz.onclick = ()=>{
quiz_box.classList.add("activeQuiz"); //show quiz box
result_box.classList.remove("activeResult"); //hide result box
timeValue = 15;
que_count = 0;
que_numb = 1;
userScore = 0;
widthValue = 0;
showQuetions(que_count); //calling showQestions function
queCounter(que_numb); //passing que_numb value to queCounter
clearInterval(counter); //clear counter
clearInterval(counterLine); //clear counterLine
startTimer(timeValue); //calling startTimer function
startTimerLine(widthValue); //calling startTimerLine function
timeText.textContent = "Time Left"; //change the text of timeText to Time Left
next_btn.classList.remove("show"); //hide the next button
}
// if quitQuiz button clicked
quit_quiz.onclick = ()=>{
window.location.reload(); //reload the current window
}
const next_btn = document.querySelector("footer .next_btn");
const bottom_ques_counter = document.querySelector("footer .total_que");
// if Next Que button clicked
next_btn.onclick = ()=>{
if(que_count < questions.length - 1){ //if question count is less than total question length
que_count++; //increment the que_count value
que_numb++; //increment the que_numb value
showQuetions(que_count); //calling showQestions function
queCounter(que_numb); //passing que_numb value to queCounter
clearInterval(counter); //clear counter
clearInterval(counterLine); //clear counterLine
startTimer(timeValue); //calling startTimer function
startTimerLine(widthValue); //calling startTimerLine function
timeText.textContent = "Time Left"; //change the timeText to Time Left
next_btn.classList.remove("show"); //hide the next button
}else{
clearInterval(counter); //clear counter
clearInterval(counterLine); //clear counterLine
showResult(); //calling showResult function
}
}
// getting questions and options from array
function showQuetions(index){
const que_text = document.querySelector(".que_text");
//creating a new span and div tag for question and option and passing the value using array index
let que_tag = '<span>'+ questions[index].numb + ". " + questions[index].question +'</span>';
let option_tag = '<div class="option"><span>'+ questions[index].options[0] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[1] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[2] +'</span></div>'
+ '<div class="option"><span>'+ questions[index].options[3] +'</span></div>';
que_text.innerHTML = que_tag; //adding new span tag inside que_tag
option_list.innerHTML = option_tag; //adding new div tag inside option_tag
const option = option_list.querySelectorAll(".option");
// set onclick attribute to all available options
for(i=0; i < option.length; i++){
option[i].setAttribute("onclick", "optionSelected(this)");
}
}
// creating the new div tags which for icons
let tickIconTag = '<div class="icon tick"><i class="fas fa-check"></i></div>';
let crossIconTag = '<div class="icon cross"><i class="fas fa-times"></i></div>';
//if user clicked on option
function optionSelected(answer){
clearInterval(counter); //clear counter
clearInterval(counterLine); //clear counterLine
let userAns = answer.textContent; //getting user selected option
let correcAns = questions[que_count].answer; //getting correct answer from array
const allOptions = option_list.children.length; //getting all option items
if(userAns == correcAns){ //if user selected option is equal to array's correct answer
userScore += 1; //upgrading score value with 1
answer.classList.add("correct"); //adding green color to correct selected option
answer.insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to correct selected option
console.log("Correct Answer");
console.log("Your correct answers = " + userScore);
}else{
answer.classList.add("incorrect"); //adding red color to correct selected option
answer.insertAdjacentHTML("beforeend", crossIconTag); //adding cross icon to correct selected option
console.log("Wrong Answer");
for(i=0; i < allOptions; i++){
if(option_list.children[i].textContent == correcAns){ //if there is an option which is matched to an array answer
option_list.children[i].setAttribute("class", "option correct"); //adding green color to matched option
option_list.children[i].insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to matched option
console.log("Auto selected correct answer.");
}
}
}
for(i=0; i < allOptions; i++){
option_list.children[i].classList.add("disabled"); //once user select an option then disabled all options
}
next_btn.classList.add("show"); //show the next button if user selected any option
}
function showResult(){
info_box.classList.remove("activeInfo"); //hide info box
quiz_box.classList.remove("activeQuiz"); //hide quiz box
result_box.classList.add("activeResult"); //show result box
const scoreText = result_box.querySelector(".score_text");
if (userScore > 3){ // if user scored more than 3
//creating a new span tag and passing the user score number and total question number
let scoreTag = '<span>and congrats! 🎉, You got <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag; //adding new span tag inside score_Text
}
else if(userScore > 1){ // if user scored more than 1
let scoreTag = '<span>and nice 😎, You got <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag;
}
else{ // if user scored less than 1
let scoreTag = '<span>and sorry 😐, You got only <p>'+ userScore +'</p> out of <p>'+ questions.length +'</p></span>';
scoreText.innerHTML = scoreTag;
}
}
function startTimer(time){
counter = setInterval(timer, 1000);
function timer(){
timeCount.textContent = time; //changing the value of timeCount with time value
time--; //decrement the time value
if(time < 9){ //if timer is less than 9
let addZero = timeCount.textContent;
timeCount.textContent = "0" + addZero; //add a 0 before time value
}
if(time < 0){ //if timer is less than 0
clearInterval(counter); //clear counter
timeText.textContent = "Time Off"; //change the time text to time off
const allOptions = option_list.children.length; //getting all option items
let correcAns = questions[que_count].answer; //getting correct answer from array
for(i=0; i < allOptions; i++){
if(option_list.children[i].textContent == correcAns){ //if there is an option which is matched to an array answer
option_list.children[i].setAttribute("class", "option correct"); //adding green color to matched option
option_list.children[i].insertAdjacentHTML("beforeend", tickIconTag); //adding tick icon to matched option
console.log("Time Off: Auto selected correct answer.");
}
}
for(i=0; i < allOptions; i++){
option_list.children[i].classList.add("disabled"); //once user select an option then disabled all options
}
next_btn.classList.add("show"); //show the next button if user selected any option
}
}
}
function startTimerLine(time){
counterLine = setInterval(timer, 29);
function timer(){
time += 1; //upgrading time value with 1
time_line.style.width = time + "px"; //increasing width of time_line with px by time value
if(time > 549){ //if time value is greater than 549
clearInterval(counterLine); //clear counterLine
}
}
}
function queCounter(index){
//creating a new span tag and passing the question number and total question
let totalQueCounTag = '<span><p>'+ index +'</p> of <p>'+ questions.length +'</p> Questions</span>';
bottom_ques_counter.innerHTML = totalQueCounTag; //adding new span tag inside bottom_ques_counter
}
questions.js
// creating an array and passing the number, questions, options, and answers
let questions = [
{
numb: 1,
question: "What does HTML stand for?",
answer: "Hyper Text Markup Language",
options: [
"Hyper Text Preprocessor",
"Hyper Text Markup Language",
"Hyper Text Multiple Language",
"Hyper Tool Multi Language"
]
},
{
numb: 2,
question: "What does CSS stand for?",
answer: "Cascading Style Sheet",
options: [
"Common Style Sheet",
"Colorful Style Sheet",
"Computer Style Sheet",
"Cascading Style Sheet"
]
},
{
numb: 3,
question: "What does PHP stand for?",
answer: "Hypertext Preprocessor",
options: [
"Hypertext Preprocessor",
"Hypertext Programming",
"Hypertext Preprogramming",
"Hometext Preprocessor"
]
},
{
numb: 4,
question: "What does SQL stand for?",
answer: "Structured Query Language",
options: [
"Stylish Question Language",
"Stylesheet Query Language",
"Statement Question Language",
"Structured Query Language"
]
},
{
numb: 5,
question: "What does XML stand for?",
answer: "eXtensible Markup Language",
options: [
"eXtensible Markup Language",
"eXecutable Multiple Language",
"eXTra Multi-Program Language",
"eXamine Multiple Language"
]
},
];