Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ <h1>JavaScript Quiz</h1>
<div class="content">
<div id="result"></div>
</div>
<!-- The below 'Restart Quiz' button is commented out because it is not used initially -->
<!-- <button id="restartButton" class="button-secondary">Restart Quiz</button> -->
<button id="restartButton" class="button-secondary">Restart Quiz</button>
</div>
</div>

Expand All @@ -51,4 +50,4 @@ <h1>JavaScript Quiz</h1>
<script src="src/index.js"></script>

</body>
</html>
</html>
102 changes: 102 additions & 0 deletions requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# JavaScript Quiz Project Requirements

## Overview
This application is a small quiz game with a countdown timer. The quiz should display multiple-choice questions one at a time, track the correct answers, and show an end screen after the quiz completes or the timer reaches zero.

## App Structure
- Single-page quiz experience with two main views:
- `quizView`: visible during the quiz questions
- `endView`: visible after the quiz ends
- A timer display should appear in the top-right corner of the quiz view.
- The quiz should be implemented with JavaScript and should not require page reloads to move between questions or show results.

## UI Layout and Elements
### Quiz View
- Progress bar at the top that updates to reflect current question progress.
- Question count text such as `Question 1 of 5`.
- Question text displayed clearly.
- A list of radio button choices for the current question.
- A visible timer section showing the remaining time in `MM:SS` format.
- A button labeled `Answer` to submit the current answer and move to the next question.

### End View
- Hidden by default until the quiz ends.
- Displays final score text like `You scored X out of Y correct answers!`.
- Displays a horizontal progress bar representing score percentage.
- A `Restart Quiz` button that resets the quiz and timer.

## Functional Behavior
### Quiz Data and Initialization
- The quiz should contain an array of `Question` objects.
- Each question includes:
- question text
- an array of choices
- the correct answer value
- a difficulty level (optional)
- Quiz state includes:
- `questions`
- `currentQuestionIndex`
- `correctAnswers`
- `timeLimit`
- `timeRemaining`
- When the app loads:
- shuffle the question order
- show the first question
- initialize the timer display to the quiz duration
- start the countdown

### Question Display and Answering
- Each question must display current text and choices.
- Choices should be radio inputs sharing the same `name` so only one can be selected.
- When `Answer` is clicked:
- read the selected radio choice
- if an answer is selected, validate it against the current question's answer
- increment `correctAnswers` only for correct answers
- move to the next question
- display the next question or show results if the quiz has ended
- If no answer is selected, the app may do nothing or simply not advance.

### Progress Tracking
- The progress bar width should reflect the portion of answered questions, e.g. `((currentQuestionIndex + 1) / totalQuestions) * 100%`.
- The question count text should always use the current question number and total questions.

## Timer Requirements
- The timer starts when the quiz begins.
- It counts down from `timeRemaining` once per second using `setInterval()`.
- Timer text must update every second in `MM:SS` format.
- If the timer reaches zero:
- stop the timer interval
- set `timeRemaining` to 0
- automatically end the quiz and show the result view
- If the quiz ends before time runs out:
- clear the timer interval immediately to prevent background execution

## End-of-Quiz Behavior
- The quiz ends when:
- the last question has been answered, or
- the timer reaches zero.
- When the quiz ends:
- hide the quiz view
- show the end view
- clear the timer interval
- show the user score text
- update the result progress bar based on score percentage

## Restart Behavior
- When `Restart Quiz` is clicked:
- hide the end view
- show the quiz view
- reset `currentQuestionIndex` to 0
- reset `correctAnswers` to 0
- reset `timeRemaining` to `timeLimit`
- shuffle questions again
- show the first question
- update the timer display to the full duration
- start the countdown timer again

## Additional Notes
- Keep all application logic inside `src/index.js`.
- `src/quiz.js` should define the `Quiz` class and state methods like `getQuestion()`, `moveToNextQuestion()`, `checkAnswer()`, and `hasEnded()`.
- `src/question.js` should define the `Question` class, including any helper methods such as `shuffleChoices()` if needed.
- Ensure the timer display is accurate and responsive when restarting the quiz.
- Avoid unnecessary timers or memory leaks by always clearing intervals when they are no longer needed.
133 changes: 110 additions & 23 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ document.addEventListener("DOMContentLoaded", () => {

// End view elements
const resultContainer = document.querySelector("#result");
const resultProgressBar = document.querySelector("#resultProgressBar");
const restartButton = document.querySelector("#restartButton");


/************ SET VISIBILITY OF VIEWS ************/
Expand All @@ -29,8 +31,9 @@ document.addEventListener("DOMContentLoaded", () => {
new Question("What is 2 + 2?", ["3", "4", "5", "6"], "4", 1),
new Question("What is the capital of France?", ["Miami", "Paris", "Oslo", "Rome"], "Paris", 1),
new Question("Who created JavaScript?", ["Plato", "Brendan Eich", "Lea Verou", "Bill Gates"], "Brendan Eich", 2),
new Question("What is the mass–energy equivalence equation?", ["E = mc^2", "E = m*c^2", "E = m*c^3", "E = m*c"], "E = mc^2", 3),
new Question("What is the mass–energy equivalence equation?", ["E = mc^2", "E = m*c^2", "E = m*c^3", "E = m*c"], "E = m*c^2", 3),
// Add more questions here
new Question("Where is France", ["in Europe", "In Asia", "in Africa", "in South America"], "in Europe", 2),
];
const quizDuration = 120; // 120 seconds (2 minutes)

Expand All @@ -45,26 +48,58 @@ document.addEventListener("DOMContentLoaded", () => {

/************ SHOW INITIAL CONTENT ************/

// Convert the time remaining in seconds to minutes and seconds, and pad the numbers with zeros if needed
const minutes = Math.floor(quiz.timeRemaining / 60).toString().padStart(2, "0");
const seconds = (quiz.timeRemaining % 60).toString().padStart(2, "0");

// Display the time remaining in the time remaining container
// Get the timer display container and its span element
const timeRemainingContainer = document.getElementById("timeRemaining");
timeRemainingContainer.innerText = `${minutes}:${seconds}`;
const timeRemainingSpan = timeRemainingContainer.querySelector("span");

// Show first question
showQuestion();
let timer;

function formatTime(seconds) {
const minutes = Math.floor(seconds / 60).toString().padStart(2, "0");
const remainingSeconds = (seconds % 60).toString().padStart(2, "0");
return `${minutes}:${remainingSeconds}`;
}

function updateTimerDisplay() {
timeRemainingSpan.innerText = formatTime(quiz.timeRemaining);
}

/************ TIMER ************/
function startTimer() {
if (timer) {
clearInterval(timer);
}

updateTimerDisplay();

timer = setInterval(() => {
quiz.timeRemaining -= 1;

if (quiz.timeRemaining <= 0) {
quiz.timeRemaining = 0;
updateTimerDisplay();
clearInterval(timer);

timer = null;
showResults();
return;
}

updateTimerDisplay();
}, 1000);
}

updateTimerDisplay();

// Show first question
showQuestion();
startTimer();

let timer;


/************ EVENT LISTENERS ************/

nextButton.addEventListener("click", nextButtonHandler);
restartButton.addEventListener("click", restartButtonHandler);



Expand Down Expand Up @@ -98,21 +133,23 @@ document.addEventListener("DOMContentLoaded", () => {
//
// 1. Show the question
// Update the inner text of the question container element and show the question text

questionContainer.innerText = question.text;

// 2. Update the green progress bar
// Update the green progress bar (div#progressBar) width so that it shows the percentage of questions answered

progressBar.style.width = `65%`; // This value is hardcoded as a placeholder

// const progress = (quiz.currentQuestionIndex + 1) * 10;
const progress = ((quiz.currentQuestionIndex + 1) / quiz.questions.length) * 100;
// console.log(`progress = ${Math.round(progress)}%`)

// progressBar.style.width = `25%`; // This value is hardcoded as a placeholder
progressBar.style.width = `${Math.round(progress)}%`

// 3. Update the question count text
// Update the question count (div#questionCount) show the current question out of total questions

questionCount.innerText = `Question 1 of 10`; // This value is hardcoded as a placeholder


// questionCount.innerText = `Question ${quiz.currentQuestionIndex + 1} of 10`; // This value is hardcoded as a placeholder
questionCount.innerText = `Question ${quiz.currentQuestionIndex + 1} of ${quiz.questions.length}`;

// 4. Create and display new radio input element with a label for each choice.
// Loop through the current question `choices`.
Expand All @@ -128,6 +165,21 @@ document.addEventListener("DOMContentLoaded", () => {
// Hint 3: You can use the `element.appendChild()` method to append an element to the choices container.
// Hint 4: You can use the `element.innerText` property to set the inner text of an element.

question.choices.forEach((choice) => {
const choiceInput = document.createElement("input");
choiceInput.type = "radio";
choiceInput.name = "choice";
choiceInput.value = choice;

const choiceLabel = document.createElement("label");
choiceLabel.innerText = choice;

const lineBreak = document.createElement("br");

choiceContainer.appendChild(choiceInput);
choiceContainer.appendChild(choiceLabel);
choiceContainer.appendChild(lineBreak);
});
}


Expand All @@ -140,35 +192,70 @@ document.addEventListener("DOMContentLoaded", () => {
// YOUR CODE HERE:
//
// 1. Get all the choice elements. You can use the `document.querySelectorAll()` method.
console.log("Get all the choice elements = ")
const choiceElements = document.querySelectorAll('input[name="choice"]');

console.log(choiceElements);

// 2. Loop through all the choice elements and check which one is selected
// Hint: Radio input elements have a property `.checked` (e.g., `element.checked`).
// When a radio input gets selected the `.checked` property will be set to true.
// You can use check which choice was selected by checking if the `.checked` property is true.

choiceElements.forEach((radio) => {
if (radio.checked) {
selectedAnswer = radio.value;
}
});

// 3. If an answer is selected (`selectedAnswer`), check if it is correct and move to the next question
// Check if selected answer is correct by calling the quiz method `checkAnswer()` with the selected answer.
// Move to the next question by calling the quiz method `moveToNextQuestion()`.
// Show the next question by calling the function `showQuestion()`.
if (selectedAnswer) {
quiz.checkAnswer(selectedAnswer);
quiz.moveToNextQuestion();
showQuestion();
}
}




function showResults() {
if (timer) {
clearInterval(timer);
timer = null;
}

// YOUR CODE HERE:
//
// 1. Hide the quiz view (div#quizView)
quizView.style.display = "none";

// 2. Show the end view (div#endView)
endView.style.display = "flex";

// 3. Update the result container (div#result) inner text to show the number of correct answers out of total questions
resultContainer.innerText = `You scored 1 out of 1 correct answers!`; // This value is hardcoded as a placeholder
resultContainer.innerText = `You scored ${quiz.correctAnswers} out of ${quiz.questions.length} correct answers!`;

const resultPercentage = (quiz.correctAnswers / quiz.questions.length) * 100;
resultProgressBar.style.width = `${resultPercentage}%`;
}



function restartButtonHandler() {
// Hide the end view and show the quiz view again
endView.style.display = "none";
quizView.style.display = "block";

// Reset the quiz state
quiz.currentQuestionIndex = 0;
quiz.correctAnswers = 0;
quiz.timeRemaining = quiz.timeLimit;

// Shuffle the questions and show the first question
quiz.shuffleQuestions();
showQuestion();
startTimer();
}

});
});
24 changes: 24 additions & 0 deletions src/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@ class Question {
// YOUR CODE HERE:
//
// 1. constructor (text, choices, answer, difficulty)
constructor(text, choices, answer, difficulty) {
this.text = text
this.choices = choices
this.answer = answer
// difficulty: with 1 being the easiest and 3 being the hardest.
this.difficulty = difficulty
//console.log("choices = ", choices)
}

// 2. shuffleChoices()
shuffleChoices() {
console.log("choices = ", this.choices)

for(let i=0; i < this.choices.length; i++) {
// console.log("Choise = ", this.choices[i])
const j = Math.floor(Math.random() * this.choices.length);

// swap elements
const val = this.choices[i]
this.choices[i] = this.choices[j]
this.choices[j] = val
}

// console.log("Choise = ", this.choices)
}

}
Loading