Bài 12: Mini Project – Quiz trắc nghiệm (có chấm điểm)
🎯 Mục tiêu
- Biết tạo câu hỏi trắc nghiệm bằng HTML (radio)
- Biết xử lý chấm điểm bằng JavaScript
- Hiểu cách lưu dữ liệu câu hỏi trong mảng (array) và đối tượng (object)
- Biết cập nhật giao diện bằng DOM
📘 Mô tả bài toán
Bạn sẽ làm một trang Quiz có 5 câu hỏi:
- Chọn đáp án (A/B/C/D)
- Bấm “Nộp bài” để chấm điểm
- Hiển thị số câu đúng + tổng điểm
- Tô màu câu đúng/sai
- (Nâng cao) Có nút “Làm lại”
📘 Kiến thức dùng trong bài
- Radio: chỉ chọn 1 đáp án trong một nhóm (cùng
name). - Array/Object: lưu danh sách câu hỏi để dễ mở rộng.
- DOM: JS đọc đáp án người dùng chọn và cập nhật kết quả.
💻 Code mẫu (Quiz hoàn chỉnh)
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Bài 12 - Quiz trắc nghiệm</title>
<style>
body{
font-family: Arial;
background:#f7f8fa;
padding: 24px;
line-height: 1.7;
}
.app{
max-width: 760px;
margin: 0 auto;
background:#fff;
border:1px solid #e5e5e5;
border-radius: 14px;
padding: 18px;
}
h1{ margin-top:0; text-align:center; }
.q{
border:1px solid #e5e5e5;
border-radius: 12px;
padding: 14px;
margin: 12px 0;
background:#fff;
}
.q.correct{ background:#ecfdf5; border-color:#bbf7d0; }
.q.wrong{ background:#fff1f2; border-color:#fecdd3; }
.answers label{
display:block;
padding: 6px 0;
cursor:pointer;
}
.row{
display:flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
margin-top: 14px;
}
button{
padding: 10px 14px;
border:0;
border-radius: 10px;
cursor:pointer;
background:#2563eb;
color:#fff;
font-weight: 700;
}
button.gray{ background:#111; }
.result{
margin-top: 14px;
padding: 12px;
border-radius: 12px;
background:#f3f4f6;
border:1px solid #e5e7eb;
}
small{ color:#555; }
</style>
</head>
<body>
<div class="app">
<h1>🧠 Quiz trắc nghiệm</h1>
<p>Chọn đáp án đúng rồi bấm <b>Nộp bài</b>.</p>
<div id="quiz"></div>
<div class="row">
<button id="btnSubmit">Nộp bài</button>
<button class="gray" id="btnReset">Làm lại</button>
</div>
<div class="result" id="result">
<small>Kết quả sẽ hiển thị ở đây.</small>
</div>
</div>
<script>
// 1) Dữ liệu câu hỏi (có thể thêm bớt dễ dàng)
const questions = [
{
text: "HTML dùng để làm gì?",
options: ["Tạo cấu trúc", "Làm đẹp", "Tạo tương tác", "Lưu dữ liệu"],
answer: 0
},
{
text: "CSS dùng để làm gì?",
options: ["Chấm điểm", "Làm đẹp giao diện", "Tạo server", "Tạo database"],
answer: 1
},
{
text: "Thẻ nào tạo liên kết?",
options: ["<img>", "<a>", "<p>", "<div>"],
answer: 1
},
{
text: "Trong JS, từ khóa khai báo biến có thể đổi giá trị là?",
options: ["const", "let", "varrr", "fixed"],
answer: 1
},
{
text: "Thuộc tính nào bắt buộc nhập form?",
options: ["required", "checked", "href", "alt"],
answer: 0
}
];
const quizEl = document.getElementById("quiz");
const resultEl = document.getElementById("result");
const btnSubmit = document.getElementById("btnSubmit");
const btnReset = document.getElementById("btnReset");
// 2) Render câu hỏi ra HTML
function renderQuiz(){
quizEl.innerHTML = "";
questions.forEach((q, i) => {
const box = document.createElement("div");
box.className = "q";
box.setAttribute("data-index", i);
let html = "<h3>Câu " + (i+1) + ": " + q.text + "</h3>";
html += '<div class="answers">';
q.options.forEach((opt, j) => {
html +=
'<label>' +
'<input type="radio" name="q' + i + '" value="' + j + '"> ' +
opt +
'</label>';
});
html += "</div>";
box.innerHTML = html;
quizEl.appendChild(box);
});
resultEl.innerHTML = "<small>Kết quả sẽ hiển thị ở đây.</small>";
}
// 3) Chấm điểm
function grade(){
let correct = 0;
const boxes = document.querySelectorAll(".q");
boxes.forEach((box, i) => {
box.classList.remove("correct", "wrong");
const checked = document.querySelector('input[name="q' + i + '"]:checked');
const userAnswer = checked ? Number(checked.value) : -1;
if(userAnswer === questions[i].answer){
correct++;
box.classList.add("correct");
} else {
box.classList.add("wrong");
}
});
const total = questions.length;
const score = Math.round((correct / total) * 10);
resultEl.innerHTML =
"✅ Đúng: <b>" + correct + "/" + total + "</b> câu" +
"<br>🎯 Điểm: <b>" + score + "/10</b>";
}
// 4) Reset
function resetQuiz(){
renderQuiz();
}
btnSubmit.addEventListener("click", grade);
btnReset.addEventListener("click", resetQuiz);
// chạy lần đầu
renderQuiz();
</script>
</body>
</html>
👉 Copy code → lưu thành bai12.html → mở bằng Chrome.
Bấm “Nộp bài” để chấm điểm và xem câu đúng/sai được tô màu.
✍️ Bài tập
- Thêm 3 câu hỏi mới vào mảng
questions. - Đổi thang điểm từ 10 sang 100.
- (Nâng cao) Nếu người học bỏ trống câu nào, thông báo “Bạn chưa làm hết”.
- (Nâng cao) Hiển thị đáp án đúng sau khi nộp bài.
✅ Đáp án gợi ý
1) Thang điểm 100
const score = Math.round((correct / total) * 100);
2) Kiểm tra bỏ trống
let unanswered = 0;
const checked = document.querySelector('input[name="q' + i + '"]:checked');
if(!checked) unanswered++;
if(unanswered > 0){
resultEl.innerHTML = "⚠️ Bạn chưa làm hết: " + unanswered + " câu.";
return;
}
3) Hiển thị đáp án đúng (gợi ý)
// Sau khi chấm, bạn có thể thêm dòng:
box.innerHTML += "<p><b>Đáp án đúng:</b> " + questions[i].options[questions[i].answer] + "</p>";
📌 Danh sách bình luận