How to Build a Simple Quiz App with a Timer Using HTML, CSS, and JavaScript?
In this tutorial, we will create a simple Quiz Application with Timer using HTML, CSS, and JavaScript. This step-by-step guide aims to provide you with a strong foundation and a practical example to kickstart your journey in developing similar interactive applications. By the end of this tutorial, you'll have a functional quiz app with a countdown timer to enhance user engagement.
What Does the Quiz Application with Timer Do?
The Simple Quiz Application with Timer is a user-friendly and interactive tool designed to test knowledge with a time-bound challenge. This application presents users with a quiz containing 5 multiple-choice questions. Users must complete the quiz within a time limit of 2 minutes and 30 seconds. If the timer runs out, the application automatically displays the results, even if some questions are left unanswered.
As users select their answers, the application instantly checks their response. Correct answers are highlighted with a green background, while incorrect choices are marked with a red background. This immediate feedback enhances the learning experience, making the quiz engaging and informative.
Let's start the coding part!
Step 1: Creating a JSON File
First, let's create a new JSON file. This file will store the data for the quiz questions that will be presented to the users. The JSON structure consists of objects with the following keys:
- id: A unique identifier for each question.
- question: The text of the question to be displayed.
- choices: An array of possible answers.
- answer: The correct answer, represented by the index of the correct choice in the choices array.
Below is an example of a JSON data structure created for this tutorial to define the quiz questions and answers:
data.json
- [
- {
- "id": 1,
- "question": "Which of the following is a JavaScript framework?",
- "choices": ["Django", "React", "Flask", "Ruby"],
- "answer": 1
- },
- {
- "id": 2,
- "question": "What does HTML stand for?",
- "choices": ["Hyper Text Markup Language", "High Transfer Markup Language", "Hyperlinks and Text Markup Language", "Hyper Transfer Machine Language"],
- "answer": 0
- },
- {
- "id": 3,
- "question": "Which of the following is used to declare a variable in Python?",
- "choices": ["var", "let", "def", "None of the above"],
- "answer": 3
- },
- {
- "id": 4,
- "question": "What is the output of console.log(typeof null) in JavaScript?",
- "choices": ["null", "undefined", "object", "string"],
- "answer": 2
- },
- {
- "id": 5,
- "question": "Which of these is not a valid CSS property?",
- "choices": ["color", "font-size", "text-align", "print-mode"],
- "answer": 3
- }
- ]
Step 2: Creating the Interface
Next, let's create the HTML file for our Quiz Application. This file will contain the essential elements for the application's structure, such as the header, timer, and placeholders for questions and choices. Note that the actual questions and choices will be dynamically generated and are not included directly in this file. Below is an example of the HTML code created for this tutorial:
index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" />
- <link rel="stylesheet" href="style.css">
- </head>
- <body>
- <div id="main-wrapper">
- <div class="flex-container align-center justify-center w-100">
- </div>
- <div id="quiz-container">
- <div class="flex-container align-center justify-between w-100">
- <div class="flex-grow-1">
- </div>
- <div id="timer-container" class="flex-container align-center justify-center">
- </div>
- </div>
- <hr class="hr-light">
- <div id="quiz-actions" class="flex-container w-100 align-center justify-end">
- </div>
- </div>
- <div id="quiz-result">
- <div id="quiz-result-text">
- </div>
- <div class="flex-container align-center justify-center w-100">
- </div>
- </div>
- </div>
- </body>
- </html>
Step 3: Designing the Interface
Now, let's create the custom Cascading Stylesheet (CSS) for our application. This file defines the visual design and layout of the application's elements, ensuring a user-friendly and visually appealing interface. Below is the CSS code crafted for this tutorial:
style.css
- @import url('https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap');
- * {
- font-family: "Ubuntu", serif;
- font-weight: 400;
- font-style: normal;
- box-sizing: border-box;
- }
- html, body {
- margin: unset;
- padding: unset;
- height: 100%;
- min-height: 100%;
- width: 100%;
- min-width: 100%;
- overflow: auto;
- }
- body {
- display: flex;
- flex-flow: column wrap;
- justify-content: center;
- align-items: center;
- background-color: #4290fb;
- background-image: linear-gradient( 89.5deg, rgba(131,204,255,1) 0.4%, rgba(66,144,251,1) 100.3% );
- }
- .fs-light {
- font-weight: 300;
- }
- .fs-midium {
- font-weight: 500;
- }
- .fs-bold {
- font-weight: 700;
- }
- #main-wrapper{
- max-height: 100%;
- width: 100%;
- max-width: 500px;
- padding: 15px 15px;
- }
- #app-title{
- font-size: 20px;
- color: #fff;
- font-weight: 700;
- letter-spacing: 1px;
- text-align: center;
- margin-bottom: 10px;
- }
- .flex-container{
- display: flex;
- }
- .w-100{
- width: 100%;
- }
- .align-center{
- align-items: center;
- }
- .justify-center{
- justify-content: center;
- }
- .justify-between{
- justify-content: space-between;
- }
- .justify-end{
- justify-content: end;
- }
- .flex-grow-1{
- flex-grow: 1;
- }
- hr.hr-light {
- border-color: #ffffff2b;
- }
- #start-button,
- #restart-button {
- display: flex;
- align-items: center;
- justify-content: center;
- background: #3D3BF3;
- color: #fff;
- border: unset;
- outline: unset;
- padding: 5px 30px;
- border-radius: 3px;
- font-weight: 600;
- letter-spacing: 1.2px;
- cursor: pointer;
- margin-bottom: 20px;
- }
- #start-button:hover,
- #start-button:active,
- #restart-button:hover,
- #restart-button:active
- {
- background: #0200b6;
- }
- #quiz-container{
- display: none;
- width: 100%;
- padding: 15px 15px;
- background: #fff;
- border-radius: 5px;
- box-shadow: 2px 5px 7px #00000068;
- color: #494949;
- }
- #timer{
- font-weight: 600;
- }
- div#quiz-list {
- width: 100%;
- padding: 15px 10px;
- overflow: hidden;
- /* position: relative; */
- display: flex;
- }
- #quiz-list .quiz-item{
- width: 100%;
- min-height: 100%;
- flex: none;
- background-color: #fff;
- }
- #quiz-list.trans-prev .quiz-item:not(.item-trans){
- transform: translateX(-100%);
- }
- #quiz-list.trans-next .quiz-item.item-trans{
- transform: translateX(0%);
- }
- #quiz-list.trans-prev .quiz-item.item-trans{
- transform: translateX(-100%);
- }
- #quiz-list .quiz-item.animate-trans{
- transition: all .5s ease-in-out;
- }
- #quiz-list.trans-next .quiz-item.animate-trans.start.item-trans {
- transform: translateX(-100%);
- }
- #quiz-list.trans-prev .quiz-item.animate-trans.start.item-trans {
- transform: translateX(100%);
- }
- #quiz-list.trans-next .quiz-item.animate-trans.start:not(.item-trans) {
- transform: translateX(-100%);
- }
- #quiz-list.trans-prev .quiz-item.animate-trans.start:not(.item-trans) {
- transform: translateX(100%);
- }
- #quiz-list .quiz-item .quiz-item-question{
- font-size: 14px;
- padding: 20px 5px;
- }
- #quiz-list .quiz-item .quiz-item-choices{
- width: 100%;
- }
- #quiz-list .quiz-item .quiz-choice{
- display: block;
- width: 100%;
- padding: 10px 10px;
- text-decoration: none;
- color: inherit;
- border: 1px solid #49494929;
- border-radius: 3px;
- margin-bottom: 10px;
- box-shadow: 2px 3px 5px #0000002b;
- transition: all .2s ease-in-out;
- }
- #quiz-list .quiz-item .quiz-choice:hover {
- transform: translateY(-2px);
- background: #f6f6f6;
- }
- #quiz-list .quiz-item .quiz-choice.correct {
- transform: translateY(-2px);
- background: #52e4882b;
- }
- #quiz-list .quiz-item .quiz-choice.wrong {
- transform: translateY(-2px);
- background: #e45c522b;
- }
- #quiz-list .quiz-item .quiz-choice.disabled{
- user-select: none;
- cursor: not-allowed;
- }
- button.quiz-action-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- margin: 0 5px;
- border: unset;
- border-radius: 3px;
- background: #0A5EB0;
- color: #fff;
- padding: 5px 15px;
- cursor: pointer;
- }
- #quiz-result{
- display: none;
- }
- div#quiz-result-text {
- color: #fff;
- text-align: center;
- margin: 20px 0;
- }
Step 4: Creating the Main Functions
Finally, let's create the JavaScript file. This file contains the essential JavaScript code that powers the functionality of the Quiz Application with Timer. The JavaScript manages features like loading questions, validating answers, keeping track of the timer, and displaying results. To make the code more efficient and easier to write, I have also utilized jQuery. Below is the JavaScript code for your reference:
script.js
- // Quiz Start Button
- const StartBtn = $('#start-button')
- // Quiz Restart Button
- const RestartBtn = $('#restart-button')
- // Quiz Questionnaire Container
- const Questionnaire = $('#quiz-container')
- // Quiz List Container
- const QuizList = $("#quiz-list")
- // Quiz Item Element to Clone
- const QuizItem = $(`
- <div class="quiz-item">
- <div class="quiz-item-question">Lorem ipsum dolor sit amet, consectetur adipiscing elit</div>
- <div class="quiz-item-choices">
- </div>
- </div>
- `)
- const QuizChoiceEL = $(`
- <a href="javascript:void(0)" class="quiz-choice">Choice 101</a>
- `)
- // Previous Button
- const PrevBtn = $('#prev-question-btn')
- // Next Button
- const NextBtn = $('#next-question-btn')
- // Finish Button
- const FinishBtn = $('#finish-question-btn')
- // Current Question Number
- const CurrentQNumber = $('#current_question_number')
- // Total Question Number
- const TotalQNumber = $('#total_question_number')
- // Result Container
- const ResultContainer = $('#quiz-result')
- // Result Text
- const ResultText = $('#quiz-result-text')
- // Timer Element
- const timerEL = $('#timer')
- var questions = [];
- var currentQindex = 0;
- var timerSec = 150;
- var score = 0;
- var answerArr = [];
- var TimerInterval;
- // Load JSON Data
- async function fetch_data() {
- return await fetch("data.json")
- .then(response => {
- if (!response.ok) {
- console.error(response);
- }
- return response.json();
- })
- .then(data => {
- return (data)
- })
- .catch(error => {
- console.log(error)
- })
- }
- function shuffle(arrData) {
- let curIndx = arrData.length;
- while (curIndx != 0) {
- let randIndx = Math.floor(Math.random() * curIndx);
- curIndx--;
- [arrData[curIndx], arrData[randIndx]] = [
- arrData[randIndx], arrData[curIndx]];
- }
- }
- function check_ans(el){
- el.find(".quiz-choice").click(function(e){
- e.preventDefault()
- if($(this).hasClass("disabled"))
- return;
- var choiceKey = $(this)[0].dataset.key
- var id = el[0].dataset.id
- var correct_ans;
- for(var i = 0; i < questions.length; i++){
- if(questions[i].id == id){
- correct_ans = questions[i].answer
- }
- }
- if(choiceKey == correct_ans){
- $(this).addClass("correct");
- score++;
- }else{
- $(this).addClass("wrong")
- el.find(`.quiz-choice[data-key="${correct_ans}"]`).addClass("correct")
- }
- el.find(`.quiz-choice`).addClass("disabled")
- answerArr.push({id: id, correct: correct_ans, wrong: ((choiceKey != correct_ans) ? choiceKey : "")})
- })
- }
- function showResult(){
- Questionnaire.toggle(false)
- ResultText.text(`Your Total Correct Answer is ${score} out of `+ questions.length + `.`)
- ResultContainer.toggle(true)
- clearInterval(TimerInterval)
- }
- // Check if Question Already Answered
- function chech_if_answered(el){
- var id = el[0].dataset.id;
- if(answerArr.length > 0){
- for(var i = 0; i < answerArr.length; i++){
- if(answerArr[i].id == id){
- el.find(`.quiz-choice[data-key="${answerArr[i].correct}"]`).addClass("correct")
- if(answerArr[i].wrong > 0)
- el.find(`.quiz-choice[data-key="${answerArr[i].wrong}"]`).addClass("wrong");
- el.find(`.quiz-choice`).addClass("disabled")
- }
- }
- }
- }
- // Computing and Updating Timer
- function computeTimer(time){
- var min = Math.floor(time / 60) | 0;
- var sec = Math.ceil(time - (min * 60));
- min = String(min).padStart(2, '0');
- sec = String(sec).padStart(2, '0');
- timerEL.text(`${min}:${sec}`)
- }
- function startQuiz(){
- if(!questions[currentQindex]){
- alert("Questions are not listed yet!");
- return;
- }
- currentQindex = 0;
- score = 0;
- score = 0;
- answerArr = [];
- QuizList.html('')
- CurrentQNumber.text(currentQindex+1)
- timerSec = 150;
- computeTimer(timerSec)
- StartBtn.toggle(false)
- Questionnaire.toggle(true)
- PrevBtn.toggle(false)
- NextBtn.toggle(false)
- FinishBtn.toggle(false)
- var itemEL = QuizItem.clone(true);
- itemEL[0].dataset.id = questions[currentQindex].id;
- itemEL.find(".quiz-item-question").text(questions[currentQindex].question);
- for(var i = 0; i < questions[currentQindex].choices.length; i++){
- var choiceEl = QuizChoiceEL.clone(true);
- choiceEl[0].dataset.key = i;
- choiceEl.text(questions[currentQindex].choices[i]);
- itemEL.find(".quiz-item-choices").append(choiceEl)
- }
- QuizList.append(itemEL);
- check_ans(itemEL);
- NextBtn.toggle(true);
- TimerInterval = setInterval(function(){
- timerSec--;
- computeTimer(timerSec)
- console.log(timerSec)
- if(timerSec <= 0){
- clearInterval(TimerInterval);
- showResult();
- return;
- }
- }, 1000)
- }
- $(document).ready(function () {
- // Load Data
- fetch_data().then(questionData => {
- questions = questionData;
- shuffle(questions);
- TotalQNumber.text(questions.length)
- // Starting the Quiz event
- StartBtn.click(function(e){
- e.preventDefault()
- startQuiz()
- })
- // Navigate to Next Question Event
- NextBtn.click(function(e){
- e.preventDefault()
- currentQindex++;
- CurrentQNumber.text(currentQindex+1)
- PrevBtn.attr("disabled", true)
- NextBtn.attr("disabled", true)
- FinishBtn.attr("disabled", true)
- var itemEL = QuizItem.clone(true);
- itemEL[0].dataset.id = questions[currentQindex].id;
- itemEL.find(".quiz-item-question").text(questions[currentQindex].question);
- for(var i = 0; i < questions[currentQindex].choices.length; i++){
- var choiceEl = QuizChoiceEL.clone(true);
- choiceEl[0].dataset.key = i;
- choiceEl.text(questions[currentQindex].choices[i]);
- itemEL.find(".quiz-item-choices").append(choiceEl)
- }
- itemEL.addClass("item-trans")
- QuizList.addClass("trans-next")
- QuizList.append(itemEL);
- check_ans(itemEL);
- chech_if_answered(itemEL);
- // itemEL.css("width", Questionnaire.innerWidth())
- // itemEL.siblings(".quiz-item").css("width", Questionnaire.innerWidth())
- itemEL.addClass("animate-trans")
- itemEL.siblings(".quiz-item").addClass("animate-trans")
- setTimeout(function(){
- itemEL.addClass("start")
- itemEL.siblings(".quiz-item").addClass("start")
- }, 100)
- setTimeout(function(){
- QuizList.removeClass("trans-next")
- itemEL.removeClass("item-trans")
- itemEL.removeClass("animate-trans")
- itemEL.removeClass("start")
- itemEL.siblings(".quiz-item").remove()
- PrevBtn.attr("disabled", false)
- NextBtn.attr("disabled", false)
- FinishBtn.attr("disabled", false)
- }, 500)
- PrevBtn.toggle(currentQindex > 0)
- NextBtn.toggle(currentQindex < (questions.length - 1))
- FinishBtn.toggle(currentQindex == (questions.length - 1))
- })
- // Navigate to Prev Question Event
- PrevBtn.click(function(e){
- e.preventDefault()
- currentQindex--;
- CurrentQNumber.text(currentQindex+1)
- PrevBtn.attr("disabled", true)
- NextBtn.attr("disabled", true)
- FinishBtn.attr("disabled", true)
- var itemEL = QuizItem.clone(true);
- itemEL[0].dataset.id = questions[currentQindex].id;
- itemEL.find(".quiz-item-question").text(questions[currentQindex].question);
- for(var i = 0; i < questions[currentQindex].choices.length; i++){
- var choiceEl = QuizChoiceEL.clone(true);
- choiceEl[0].dataset.key = i;
- choiceEl.text(questions[currentQindex].choices[i]);
- itemEL.find(".quiz-item-choices").append(choiceEl)
- }
- itemEL.addClass("item-trans")
- QuizList.addClass("trans-prev")
- QuizList.prepend(itemEL);
- check_ans(itemEL);
- chech_if_answered(itemEL);
- // itemEL.css("width", Questionnaire.innerWidth())
- // itemEL.siblings(".quiz-item").css("width", Questionnaire.innerWidth())
- setTimeout(function(){
- itemEL.addClass("start")
- itemEL.siblings(".quiz-item").addClass("start")
- itemEL.addClass("animate-trans")
- itemEL.siblings(".quiz-item").addClass("animate-trans")
- }, 100)
- setTimeout(function(){
- QuizList.removeClass("trans-prev")
- itemEL.removeClass("item-trans")
- itemEL.removeClass("animate-trans")
- itemEL.removeClass("start")
- itemEL.siblings(".quiz-item").remove()
- PrevBtn.attr("disabled", false)
- NextBtn.attr("disabled", false)
- FinishBtn.attr("disabled", false)
- }, 500)
- PrevBtn.toggle(currentQindex != 0)
- NextBtn.toggle(currentQindex < (questions.length - 1))
- FinishBtn.toggle(false)
- })
- FinishBtn.click(function(e){
- e.preventDefault()
- showResult()
- })
- RestartBtn.click(function(e){
- e.preventDefault()
- ResultContainer.toggle(false)
- startQuiz()
- })
- })
- })
Snapshotes
Below are some snapshots showcasing the results of the Quiz Application with Timer created using the source code provided above. These images demonstrate the application's interface and functionality, including the dynamic quiz questions, timer, and result display:
When Application Page is Loaded
Question and Answer Validation
Result
I have provided the complete source code as a downloadable zip file on this website. You can find the download button located at the end of this article. Feel free to download the source code and customize it to suit your specific project requirements or to explore and enhance its functionality.
There you have it. I hope this Quiz Application with Timer Tutorial will help you with what you are loking for and you'll find something useful for your future web application.
Explore more on this website for more Tutorials, Free Source Codes, and Articles covering various programming languages.
Happy Coding =)
Add new comment
- 48 views