Skip to content

Commit 85793ca

Browse files
committed
Feat: Use Open Trivia API for quiz questions (#56)
1 parent 0cdbe4a commit 85793ca

2 files changed

Lines changed: 70 additions & 29 deletions

File tree

data/projects.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,5 +175,13 @@
175175
"category": "Mini Data",
176176
"categoryKey": "data",
177177
"difficulty": "medium"
178-
}
178+
},
179+
{
180+
"title": "Quiz",
181+
"slug": "quiz",
182+
"description": "A dynamic quiz app using Open Trivia API with a timer and scoring.",
183+
"category": "Fun UI",
184+
"categoryKey": "fun",
185+
"difficulty": "intermediate"
186+
}
179187
]

projects/quiz/main.js

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,62 @@
1-
const TIME_LIMIT = 15; // Total time for each question
1+
const TIME_LIMIT = 15; // seconds per question
22
let timeLeft = TIME_LIMIT;
3-
let timerInterval = null; // This will store the interval ID
3+
let timerInterval = null;
44

5-
// Get the timer element from the DOM
65
const timerElement = document.getElementById("time");
76

8-
const data = [{ q: '2 + 2 = ?', a: ['3', '4', '5'], c: 1 }, { q: 'Capital of France?', a: ['Berlin', 'Paris', 'Rome'], c: 1 }];
7+
let questions = []; // API-loaded questions
98
let i = 0, score = 0;
9+
1010
const q = document.getElementById('q'),
1111
answers = document.getElementById('answers'),
1212
result = document.getElementById('result');
1313

14-
// Function to handle moving to the next question when time runs out
15-
function handleNextQuestion() {
16-
i++;
17-
render();
14+
/** Decode HTML entities from API */
15+
function decodeHTML(str) {
16+
const txt = document.createElement('textarea');
17+
txt.innerHTML = str;
18+
return txt.value;
19+
}
20+
21+
/** Shuffle array */
22+
function shuffle(arr) {
23+
return arr.sort(() => Math.random() - 0.5);
24+
}
25+
26+
/** Fetch questions from Open Trivia DB API */
27+
async function loadQuestions() {
28+
try {
29+
const res = await fetch('https://opentdb.com/api.php?amount=5&type=multiple');
30+
const data = await res.json();
31+
questions = data.results.map(q => ({
32+
q: decodeHTML(q.question),
33+
a: shuffle([decodeHTML(q.correct_answer), ...q.incorrect_answers.map(decodeHTML)]),
34+
c: null, // correct answer index
35+
correctAnswer: decodeHTML(q.correct_answer)
36+
}));
37+
// Compute correct answer index
38+
questions.forEach(qObj => {
39+
qObj.c = qObj.a.findIndex(ans => ans === qObj.correctAnswer);
40+
});
41+
} catch (err) {
42+
console.error('Failed to load questions', err);
43+
q.textContent = 'Failed to load questions 😢';
44+
answers.innerHTML = '';
45+
}
1846
}
1947

48+
/** Start timer for each question */
2049
function startTimer() {
21-
// Clear any existing timer before starting a new one
2250
clearInterval(timerInterval);
23-
24-
// Make sure the timer is visible at the start of a question
2551
timerElement.parentElement.style.display = 'block';
26-
27-
// Reset the time left for the new question
2852
timeLeft = TIME_LIMIT;
2953
timerElement.textContent = timeLeft;
30-
timerElement.parentElement.classList.remove('warning'); // Remove warning color
54+
timerElement.parentElement.classList.remove('warning');
3155

3256
timerInterval = setInterval(() => {
3357
timeLeft--;
3458
timerElement.textContent = timeLeft;
3559

36-
// Add a visual warning when time is low
3760
if (timeLeft <= 5) {
3861
timerElement.parentElement.classList.add('warning');
3962
}
@@ -45,37 +68,47 @@ function startTimer() {
4568
}, 1000);
4669
}
4770

71+
/** Move to next question */
72+
function handleNextQuestion() {
73+
i++;
74+
render();
75+
}
76+
77+
/** Render current question */
4878
function render() {
49-
if (i >= data.length) {
50-
clearInterval(timerInterval); // Stop timer at the end
51-
52-
// Hide the timer element
79+
if (!questions.length) return;
80+
81+
if (i >= questions.length) {
82+
clearInterval(timerInterval);
5383
timerElement.parentElement.style.display = 'none';
54-
55-
q.textContent = 'Done!';
84+
q.textContent = '🎉 Quiz Complete!';
5685
answers.innerHTML = '';
57-
result.textContent = `Score: ${score}/${data.length}`;
86+
result.textContent = `Score: ${score}/${questions.length}`;
5887
return;
5988
}
6089

61-
// Start the timer each time a new question is rendered
6290
startTimer();
6391

64-
const cur = data[i];
92+
const cur = questions[i];
6593
q.textContent = cur.q;
6694
answers.innerHTML = '';
95+
result.textContent = '';
96+
6797
cur.a.forEach((ans, idx) => {
6898
const b = document.createElement('button');
6999
b.textContent = ans;
70100
b.addEventListener('click', () => {
71101
clearInterval(timerInterval);
72102
if (idx === cur.c) score++;
73-
i++;
74-
render();
103+
handleNextQuestion();
75104
});
76105
answers.appendChild(b);
77106
});
78107
}
79108

80-
// Initial call to start the quiz
81-
render();
109+
/** Initialize quiz */
110+
(async function init() {
111+
result.textContent = 'Loading questions...';
112+
await loadQuestions();
113+
render();
114+
})();

0 commit comments

Comments
 (0)