Created
November 16, 2025 10:31
-
-
Save ochaton/88de622ff8d10cea59796b132305ce10 to your computer and use it in GitHub Desktop.
quiz-site
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Тестики :)</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <link rel="stylesheet" href="style.css"> | |
| </head> | |
| <body> | |
| <div class="page"> | |
| <header class="header"> | |
| <h1>Мини-тест</h1> | |
| <p class="subtitle"> | |
| Выбери <strong>ровно один</strong> правильный вариант в каждом вопросе и нажми «Проверить». | |
| </p> | |
| </header> | |
| <main> | |
| <form id="quiz-form" class="quiz"></form> | |
| <div class="actions"> | |
| <button type="button" id="check-btn">Проверить</button> | |
| <button type="button" id="reset-btn" class="secondary">Сбросить</button> | |
| </div> | |
| <div id="result" class="result" aria-live="polite"></div> | |
| </main> | |
| <footer class="footer"> | |
| <small>Просто статический сайтик без бэкенда :)</small> | |
| </footer> | |
| </div> | |
| <script src="script.js"></script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Здесь описываем вопросы | |
| const questions = [ | |
| { | |
| id: 1, | |
| text: "Какой тип страницы мы сейчас делаем?", | |
| options: [ | |
| "Тяжёлое SPA с бэкендом и БД", | |
| "Простой статический сайт на HTML/JS/CSS", | |
| "Нативное мобильное приложение" | |
| ], | |
| correctIndex: 1, | |
| explanation: "Это обычный статический сайт: файлики HTML + CSS + JS, без сервера и БД." | |
| }, | |
| { | |
| id: 2, | |
| text: "Сколько правильных ответов в каждом вопросе?", | |
| options: [ | |
| "Зависит от настроения автора", | |
| "Ровно один", | |
| "Ровно два" | |
| ], | |
| correctIndex: 1, | |
| explanation: "По условию теста: «ровно один» правильный вариант." | |
| }, | |
| { | |
| id: 3, | |
| text: "Что произойдёт при выкладке такого сайта на Netlify?", | |
| options: [ | |
| "Ничего, нужен обязательно Node.js сервер", | |
| "Сайт будет работать как есть, просто как набор статических файлов", | |
| "Netlify автоматически перепишет код на Go" | |
| ], | |
| correctIndex: 1, | |
| explanation: "Netlify отлично хостит чистую статику — HTML/JS/CSS без каких-либо доработок." | |
| } | |
| ]; | |
| // Генерация формы | |
| function renderQuiz() { | |
| const form = document.getElementById("quiz-form"); | |
| form.innerHTML = ""; | |
| questions.forEach((q, qIndex) => { | |
| const questionDiv = document.createElement("section"); | |
| questionDiv.className = "question"; | |
| questionDiv.dataset.questionId = q.id; | |
| const title = document.createElement("h2"); | |
| title.className = "question-title"; | |
| title.textContent = `${qIndex + 1}. ${q.text}`; | |
| questionDiv.appendChild(title); | |
| const list = document.createElement("ul"); | |
| list.className = "options"; | |
| q.options.forEach((optionText, optIndex) => { | |
| const li = document.createElement("li"); | |
| li.className = "option"; | |
| const label = document.createElement("label"); | |
| const input = document.createElement("input"); | |
| input.type = "radio"; | |
| input.name = `question-${qIndex}`; | |
| input.value = String(optIndex); | |
| const span = document.createElement("span"); | |
| span.textContent = optionText; | |
| label.appendChild(input); | |
| label.appendChild(span); | |
| li.appendChild(label); | |
| list.appendChild(li); | |
| }); | |
| questionDiv.appendChild(list); | |
| const explanation = document.createElement("div"); | |
| explanation.className = "question-explanation"; | |
| explanation.style.display = "none"; | |
| questionDiv.appendChild(explanation); | |
| form.appendChild(questionDiv); | |
| }); | |
| } | |
| function checkAnswers() { | |
| const form = document.getElementById("quiz-form"); | |
| const resultBlock = document.getElementById("result"); | |
| let correctCount = 0; | |
| let unansweredCount = 0; | |
| // Сначала убираем старые подсветки | |
| form.querySelectorAll(".option").forEach(opt => { | |
| opt.classList.remove("correct", "incorrect"); | |
| }); | |
| form.querySelectorAll(".question").forEach(q => { | |
| q.classList.remove("unanswered"); | |
| const expl = q.querySelector(".question-explanation"); | |
| if (expl) expl.style.display = "none"; | |
| }); | |
| questions.forEach((q, qIndex) => { | |
| const questionSection = form.querySelector( | |
| `.question[data-question-id="${q.id}"]` | |
| ); | |
| const selected = form.querySelector( | |
| `input[name="question-${qIndex}"]:checked` | |
| ); | |
| if (!selected) { | |
| unansweredCount++; | |
| questionSection.classList.add("unanswered"); | |
| return; | |
| } | |
| const selectedIndex = Number(selected.value); | |
| const optionsEls = questionSection.querySelectorAll(".option"); | |
| optionsEls.forEach((optEl, optIndex) => { | |
| if (optIndex === q.correctIndex) { | |
| optEl.classList.add("correct"); | |
| } | |
| if (optIndex === selectedIndex && selectedIndex !== q.correctIndex) { | |
| optEl.classList.add("incorrect"); | |
| } | |
| }); | |
| const explanationBlock = questionSection.querySelector( | |
| ".question-explanation" | |
| ); | |
| if (explanationBlock && q.explanation) { | |
| explanationBlock.textContent = q.explanation; | |
| explanationBlock.style.display = "block"; | |
| } | |
| if (selectedIndex === q.correctIndex) { | |
| correctCount++; | |
| } | |
| }); | |
| const total = questions.length; | |
| let message = `Результат: ${correctCount} из ${total}.`; | |
| if (unansweredCount > 0) { | |
| message += ` Не отвечено: ${unansweredCount}.`; | |
| } | |
| if (correctCount === total && unansweredCount === 0) { | |
| message += " Отлично! 🎉"; | |
| } else if (correctCount === 0) { | |
| message += " Попробуй ещё раз 🙂"; | |
| } | |
| resultBlock.textContent = message; | |
| } | |
| function resetQuiz() { | |
| const form = document.getElementById("quiz-form"); | |
| const resultBlock = document.getElementById("result"); | |
| form.reset(); | |
| resultBlock.textContent = ""; | |
| form.querySelectorAll(".option").forEach(opt => { | |
| opt.classList.remove("correct", "incorrect"); | |
| }); | |
| form.querySelectorAll(".question").forEach(q => { | |
| q.classList.remove("unanswered"); | |
| const expl = q.querySelector(".question-explanation"); | |
| if (expl) { | |
| expl.style.display = "none"; | |
| expl.textContent = ""; | |
| } | |
| }); | |
| } | |
| // Инициализация | |
| document.addEventListener("DOMContentLoaded", () => { | |
| renderQuiz(); | |
| document | |
| .getElementById("check-btn") | |
| .addEventListener("click", checkAnswers); | |
| document | |
| .getElementById("reset-btn") | |
| .addEventListener("click", resetQuiz); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| *, | |
| *::before, | |
| *::after { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| background: #f4f5fb; | |
| color: #222; | |
| } | |
| .page { | |
| min-height: 100vh; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 8px; | |
| } | |
| .subtitle { | |
| margin: 0; | |
| color: #555; | |
| font-size: 0.95rem; | |
| } | |
| .quiz { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .question { | |
| background: #fff; | |
| border-radius: 12px; | |
| padding: 16px 18px; | |
| box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04); | |
| } | |
| .question-title { | |
| margin: 0 0 8px; | |
| font-size: 1.02rem; | |
| } | |
| .options { | |
| list-style: none; | |
| margin: 0; | |
| padding: 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| } | |
| .option { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 4px 6px; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: background 0.15s ease; | |
| } | |
| .option:hover { | |
| background: #f1f3ff; | |
| } | |
| .option input[type="radio"] { | |
| cursor: pointer; | |
| } | |
| .option.correct { | |
| background: #e6f7e9; | |
| outline: 1px solid #3aa952; | |
| } | |
| .option.incorrect { | |
| background: #ffe9e9; | |
| outline: 1px solid #d93c3c; | |
| } | |
| .question.unanswered { | |
| outline: 1px dashed #f0a500; | |
| } | |
| .question-explanation { | |
| margin-top: 8px; | |
| font-size: 0.85rem; | |
| color: #555; | |
| } | |
| .actions { | |
| display: flex; | |
| gap: 8px; | |
| margin-top: 8px; | |
| } | |
| button { | |
| border: none; | |
| border-radius: 999px; | |
| padding: 10px 18px; | |
| font-size: 0.95rem; | |
| cursor: pointer; | |
| background: #4b6bff; | |
| color: #fff; | |
| font-weight: 500; | |
| transition: background 0.15s ease, transform 0.05s ease; | |
| } | |
| button:hover { | |
| background: #3954d9; | |
| } | |
| button:active { | |
| transform: scale(0.98); | |
| } | |
| button.secondary { | |
| background: #e0e2f5; | |
| color: #222; | |
| } | |
| button.secondary:hover { | |
| background: #cdd1f0; | |
| } | |
| .result { | |
| margin-top: 12px; | |
| padding: 10px 14px; | |
| border-radius: 10px; | |
| font-size: 0.95rem; | |
| background: #fff; | |
| box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04); | |
| } | |
| .footer { | |
| margin-top: auto; | |
| text-align: center; | |
| color: #888; | |
| font-size: 0.8rem; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment