Simple Quiz App using HTML, CSS and Javascript.
Tags: javascript, webdev, beginners, css
Published: 2022-09-02 22:38:04

How is it going techies, in this article we will build a complete multi-option quiz app using HTML, CSS and Javascript only.

### **PREREQUISITES**

1. Code Editor: To continue please make sure to have any code editor of your choice installed, **Visual Studio Code(VS Code)** was used in this article, but you are free to use any code editor of your choice. 
2. Web Browser: We will also need a web browser, you can use any web browser of your choice, in this article **Brave** was used.
3. Live Server(Optional): Live Server is a Visual Studio Code extension that reloads your webpage whenever the static files change. You don't have to use this extension to continue especially if you are not using VS Code.

### Setup

- Create a folder on your computer and open it with your code editor.
- Create 3 files in your workspace namely **index.html**, **style.css**, **src.js** and **question.js**. After this, your workspace should look like the image below(for vs code users). 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/565ngapgusd6aeii959m.png)
- Open your **index.html** in your preferred browser, if you are using vscode and you have live server installed, click the **Go Live** link at the bottom right corner of your code editor. Live server will open your project on the browser and reload your webpage automatically when changes are applied to your code.

#### Now let's code!

#### 1. In your **index.html** add the following
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="main-wrapper">
        <div class="quiz-container" id="quiz-container">
            
        </div>
    </div>
    <script src="questions.js"></script>
    <script src="src.js"></script>
</body>
</html>
```
#### What's going on up here?
Nothing much, the code above is a simple html markup that is used to structure our page. 
However there are some important things we should note, just before the closing head tag we have `<link rel="stylesheet" href="style.css">` this is used to include our `style.css` in our html so that we can style it easily, right before our closing body tag we have 
```html
<script src="questions.js"></script>
<script src="src.js"></script>
```
The function of the code above is to include **questions.js** and **src.js** into our html file. We are loading the questions.js before the src.js because the src.js file depends on the questions.js, we will see this further in the article.

#### 2. In your **style.css** include the following
```css
.main-wrapper{
    font-family:Georgia, 'Times New Roman', Times, serif;
    width: 100%;
    height: 90vh;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.quiz-container{
    background-color: rgb(231, 221, 221);
    box-shadow: gray 2px 2px 2px;
}
.quiz-container > div{
    margin: 20px;
}
.each-answer{
    margin: 10px 0%;
}
.prev-next, .jumper-button{
    padding: 5px 10px;
    margin: 2px 0%;
    background-color: blue;
    color: white;
    border: white 1px solid;
    cursor: pointer;
}
.active{
    background-color: white;
    color : blue;
    border: blue solid 1px;
}
.action-btns button:hover{
    background-color: white;
    color : blue;
    border: blue solid 1px;
}
.result-wrapper{
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.result-details{
    display: inline-flex;
    justify-content: center;
}
.result-details > div{
    margin: 20px;
}
```
#### What's going on up here?
The code above contains the basic design for our html page.

#### 3. In your **questions.js** file add the following code.
```javascript
const questions = [
    {
        question : "What is the name of this channel",
        options : [
            "Code Sculptor",
            "Code Builder",
            "Code Destroyer",
            "Code Maker"
        ],
        answer : "Code Sculptor"
    },
    {
        question : "1 + 1 is?",
        options : [
            "2", "3", "5", "19"
        ],
        answer : 2
    },
    {
        question : "Which of this is a programming language?",
        options : [
            "HTML",
            "CSS",
            "Javascript",
            "JSON"
        ],
        answer : "Javascript"
    },
    {
        question : "What is my name",
        options : [
            "Femi Fatokun",
            "David Shokunbi",
            "Wale Adenusi",
            "Daniel Adewale"
        ],
        answer : "Femi Fatokun"
    }
]
```
#### What's going on up here?
The code above only includes one variable **questions**, questions contains an **array** that contains **objects**. The objects are dummy questions that have the following keys :
> - `question`
> - `options` an **array** of possible answers to the questions
> - `answer` the actual answer to the question

#### 4. In your **src.js** file add the following code.
```javascript
let quizContainer = document.getElementById("quiz-container");
let selectedAnswers = {};
let question;
let index;

const generateOptions = () => {
    let options = ``;
    for (let i = 0; i < question.options.length; i++) {
        let option = question.options[i];
        let selected = false;
        if(selectedAnswers[`${question.question}`] == option){
            selected = true;
        }
        options += `<div class="each-answer">
        <input type="radio" name="answer-btn" ${selected ? "checked='true'" : ""} onclick="addAnswer()" value="${option}">
        ${option}
        </div>`;
    }
    return options;
}

const generateJumpBtns = ()=>{
    let btns = ``;
    for(let i = 0; i < questions.length; i++){
        btns += `<button class="jumper-button ${i == index ? "active" : ""}" onclick="showQuestion(${i})">${i + 1}</button>`;
    }
    return btns;
}

const addAnswer = ()=>{
    let currentOptions = document.getElementsByName("answer-btn");
    for(let i = 0; i < currentOptions.length; i++){
        let currentOption = currentOptions[i];
        if(currentOption.checked == true){
            selectedAnswers[`${question.question}`] = currentOption.value;
        }
    }
}

const restartQuiz = ()=>{
    index = 0;
    selectedAnswers = {};
    showQuestion(0);
}

const submitQuiz = ()=>{
    let message = `Are you sure you want to submit?`;
    let answered = Object.keys(selectedAnswers);
    if(answered.length < questions.length){
        message += `\nYou have ${questions.length - answered.length} questions out of ${questions.length} unanswered`;
    }
    let ask = confirm(message);
    let attempted = 0;
    let correct = 0;
    let wrong = 0;
    if(ask){
        for(let i = 0; i < questions.length; i++){
            let question = questions[i];
            if(selectedAnswers[`${question.question}`] != undefined){
                attempted++;
                if(selectedAnswers[`${question.question}`] == question.answer){
                    correct++;
                }else{
                    wrong++;
                }
            }
        }
        quizContainer.innerHTML = `<div class="result-wrapper">
        <h3>Result</h3>
        <div class="result-details">
            <div>
                <div>Attempted</div>
                <div>${attempted}/${questions.length}</div>
            </div>
            <div>
                <div>Correct</div>
                <div>${correct}/${questions.length}</div>
            </div>
            <div>
                <div>Wrong</div>
                <div>${wrong}/${questions.length}</div>
            </div>
        </div>
        <button class="jumper-button" onclick="restartQuiz()">Restart</button>
    </div>`;
    }
}

const showQuestion = (i) => {
    index = i;
    if (questions[index]) {
        question = questions[index];
        let options = generateOptions(index);
        let jumpBtns = generateJumpBtns(index);


        quizContainer.innerHTML = `
            <div class="pointer-container">
                <h3>
                    Question ${index + 1} of ${questions.length}
                </h3>
            </div>
            <div>
                ${question.question}
            </div>
            <div class="answers-container">
                ${options}
            </div>
            <div class="action-btns">
                ${index > 0 ? `<button class="prev-next" onclick="showQuestion(${index - 1})">Previous</button>` : ""}
                ${jumpBtns}
                ${index < questions.length - 1 ? `<button class="prev-next" onclick="showQuestion(${index + 1})">Next</button>` : ""}
                <button class="prev-next" style="background-color : red;" onclick="submitQuiz()">Submit</button>
            </div>`
        ;
    } else {
        alert("Invalid question");
    }
}

showQuestion(0);
```
#### What's going on up here?
The above code is responsible for the behaviour of our quiz application, the code will be explained **block for block**. Let's go.....

- **Variable declaration(initialization)**
```javascript
let quizContainer = document.getElementById("quiz-container");
let selectedAnswers = {};
let question;
let index;
```
In the code above we created 4 variables, namely :
> - `quizContainer` is assigned to the `document.getElementById()` method, `quiz-container` was passed as the aurgment of the method. The variable now represents 
`<div class="quiz-container" id="quiz-container"></div>` in our html structure.
> - `selectedAnswers` contains an empty object, `{}`, this variable will hold the answers we select while taking the test.
> - `question` was initialized without any value, the variable will carry the question at any instance during the test.
>- `index` was initialized without any value, the variable will later represent th index of the question we are currently answering in the `questions` array.

- **generateOptions** function

```javascript
const generateOptions = () => {
    let options = ``;
    for (let i = 0; i < question.options.length; i++) {
        let option = question.options[i];
        let selected = false;
        if(selectedAnswers[`${question.question}`] == option){
            selected = true;
        }
        options += `<div class="each-answer">
        <input type="radio" name="answer-btn" ${selected ? "checked='true'" : ""} onclick="addAnswer()" value="${option}">
        ${option}
        </div>`;
    }
    return options;
}
``` 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzazteh7wfhz0hosr8et.png)
The function is responsible for generating the options elements for the current question. Here is a complete breakdown of what's going on inside the function.
> - `options` variable, initialy this is just an empty string, but after our loop the variable will contain a list of elements that represents the options of a particular question.
> - Then we write a for loop to iterate through the options of the current question and then we perform the following operations.
>> - We check if an answer has been provided for that particular question before, using 

```javascript
if(selectedAnswers[`${question.question}`] == option){
            selected = true;
}
``` 

>> - Then we generate a DOM element and add it to our `options` using : 

```javascript
options += `<div class="each-answer">
        <input type="radio" name="answer-btn" ${selected ? "checked='true'" : ""} onclick="addAnswer()" value="${option}">
        ${option}
    </div>`;
``` 

>> - In the code above we added an `onclick` event listener for every option. The function will be explained later in this article.
> - At the end of our function we return the `options` variable.

- **generateJumpBtns** function

```javascript
const generateJumpBtns = ()=>{
    let btns = ``;
    for(let i = 0; i < questions.length; i++){
        btns += `<button class="jumper-button ${i == index ? "active" : ""}" onclick="showQuestion(${i})">${i + 1}</button>`;
    }
    return btns;
}

``` 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pngbz6ssxaijm1p1v3kx.png)
This function is responsible for generating the buttons that allows us to jump to any question of our choice. The operations in this function is expleained below :
> - `btns` variable, at first this function contains an empty string, but after our loop it should contains a list of elements that represents jump buttons.
> - Then we write a for loop that iterates through the length of questions available in the `questions` array. Inside our loop we performed the following operations : 
>> - Then we generate a DOM element that represents a jump button and append it to our `btns` variable, using : 
```javascript
btns += `<button class="jumper-button ${i == index ? "active" : ""}" onclick="${i != index ? `showQuestion(${i})` : ""}">${i + 1}</button>`;
``` 
>> - In the code above we added an `onclick` event listener for every option. The function will be explained later in this article.
> - At the end of our function we return the `btns` variable.

- **addAnswer** function.
```javascript
const addAnswer = ()=>{
    let currentOptions = document.getElementsByName("answer-btn");
    for(let i = 0; i < currentOptions.length; i++){
        let currentOption = currentOptions[i];
        if(currentOption.checked == true){
            selectedAnswers[`${question.question}`] = currentOption.value;
        }
    }
}
```
This function is responsibl for adding selected answer to the `selectedAnswers` object. In this case the question is the key and the answer is the value.
> - First we get all the elements will the name `answer-btn` and assign it to `currentOptions` variable, this contain an array of DOM elements that have the name `answer-btn`.
> - Then we ran a loop to check the selected answer and add the answer to the `selectedAnswers` object using the question as the `key` and the answer selected as the `value`.

- **restartQuiz** function.
```javascript
const restartQuiz = ()=>{
    index = 0;
    selectedAnswers = {};
    showQuestion(0);
}
```
The use of this function is to restart the quiz by setting the `index` to 0, `selectedAnswer` to an empty object, and executing `showQuestion(0)` to render the first question in our `questions`.

- **submitQuiz** function.
```javascript
    const submitQuiz = ()=>{
    let message = `Are you sure you want to submit?`;
    let answered = Object.keys(selectedAnswers);
    if(answered.length < questions.length){
        message += `\nYou have ${questions.length - answered.length} questions out of ${questions.length} unanswered`;
    }
    let ask = confirm(message);
    let attempted = 0;
    let correct = 0;
    let wrong = 0;
    if(ask){
        for(let i = 0; i < questions.length; i++){
            let question = questions[i];
            if(selectedAnswers[`${question.question}`] != undefined){
                attempted++;
                if(selectedAnswers[`${question.question}`] == question.answer){
                    correct++;
                }else{
                    wrong++;
                }
            }
        }
        quizContainer.innerHTML = `<div class="result-wrapper">
        <h3>Result</h3>
        <div class="result-details">
            <div>
                <div>Attempted</div>
                <div>${attempted}/${questions.length}</div>
            </div>
            <div>
                <div>Correct</div>
                <div>${correct}/${questions.length}</div>
            </div>
            <div>
                <div>Wrong</div>
                <div>${wrong}/${questions.length}</div>
            </div>
        </div>
        <button class="jumper-button" onclick="restartQuiz()">Restart</button>
    </div>`;
    }
}
```
The function of the code above is to submit the quiz at any time, below is the sequence of operations performed in the function.
> - First we created `message` variable and we assigned `Are you sure you want to submit?` to it.
> - Then we created `answered` this represents an array that contains all the keys in the `selectedAnswers` object. The keys of the object are the answered questions.
> - We then wrote a condition to check if all questions have been answered, if not we include `\nYou have ${questions.length - answered.length} questions out of ${questions.length} unanswered` in the `message` variable, this is to inform the user that they have not answered all questions.
> - We created a variable named `ask` this contains a confirmation alert using the javascript inbuilt `confirm` function and we also passed `message` as the aurgment.
> - Then we created three variable, `attempted`, `correct` and `wrong` and we set their value to `0`;
> - Then we check if the user confirms that they want to submit. If yes we run the following operations : 
>> - First we use a for loop to iterate through the `questions` array.
>> - For every iteration we run the following conditions.
>>> - First we check if the current question is available in the `selectedAnswers` object. If yes we increment the value of `attempted`. Then we check if the selected answer for that question is equal to the value of answer for that particular question in the `questions` array. If yes, we increment the value of correct. If no, we increment the value of wrong.
>> - After our loop we update the innerHTML of `quizContainer` and set the value to a markup that represents the result.

- **showQuestion** function.

```javascript
const showQuestion = (i) => {
    index = i;
    if (questions[index]) {
        question = questions[index];
        let options = generateOptions(index);
        let jumpBtns = generateJumpBtns(index);


        quizContainer.innerHTML = `
            <div class="pointer-container">
                <h3>
                    Question ${index + 1} of ${questions.length}
                </h3>
            </div>
            <div>
                ${question.question}
            </div>
            <div class="answers-container">
                ${options}
            </div>
            <div class="action-btns">
                ${index > 0 ? `<button class="prev-next" onclick="showQuestion(${index - 1})">Previous</button>` : ""}
                ${jumpBtns}
                ${index < questions.length - 1 ? `<button class="prev-next" onclick="showQuestion(${index + 1})">Next</button>` : ""}
                <button class="prev-next" style="background-color : red;" onclick="submitQuiz()">Submit</button>
            </div>`
        ;
    } else {
        alert("Invalid question");
    }
}
``` 


The duty of this function is to simple render the question at a particular index onto our page. Below is a block-to-block explanation :
> - The function recieved a parameter names `i` and we set the value of `index` to `i`, remember index is a variable that was declared without a value at the top of our code.
> - Then we check if the question at that index is available in the `questions` array. If no, we show an alert saying `Invalid question`. If yes we run the following :
>> - We set the value of `question` to the value of `questions` at `index`, remember `question` is a variable that was declared without a value at the top of our code.
>> - Then we create the `options` variable and assign it to `generateOptions(index)`, this will generate the options for the question at the given index. Therefore `options` now contains a list of option elements for the given question.
>> - After that we create `jumpBtns` variable and assign it to `generateJumpBtns(index)`, this will generate a list of jumper button elements for the list of questions available.
> - After these we update the innerHTML of the `quizContainer` with a markup that represents the current question, options and jumper buttons.
> - In our markup we also have `Previous` and `Next` button.
>> - The `Previous` button will only render if the `index` is greater than `0`, and when clicked it navigates to the previous question.
>> - The `Next` button will only render if the `index` is less that the length of the `questions`, and when clicked it navigates to the next question.

- **At the end of our code**
```javascript
showQuestion(0);
```
At the end of our file we added the code above, this is to render the first question to the screen after the page has loaded completely.

#### Watch me do this on youtube.
{% embed https://www.youtube.com/watch?v=KxKrjx51fes %}