В этом статье мы создадим простой, но функциональный квиз с вопросами и вариантами ответов. Такой квиз отлично подходит для сбора данных, проведения опросов или создания тестов на сайте. Основная идея — у пользователя есть несколько шагов, каждый из которых содержит вопрос и набор ответов на выбор.

Скачать архив с проектом и посмотреть демо вы можете здесь. Также другие проекты доступы в разделе исходники.

Вёрстка и стиль

Начнём с того, что дизайн квиза минималистичен, но при этом функционален. Всё начинается с фона: мы добавляем фоновое изображение, которое фиксируется при прокрутке страницы, создавая приятный визуальный эффект. Квиз выровнен по центру страницы и имеет ограниченную ширину, что делает его удобным для восприятия и не перегружает экран.

Кнопки, аватары, контейнеры для ответов — всё оформлено с учётом UX (пользовательского опыта). Прогресс-бар плавно изменяется с каждым шагом, помогая ориентироваться в процессе.

HTML

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="css/font.css" />
    <link rel="stylesheet" href="css/reset.css" />
    <link rel="stylesheet" href="css/base.css" />
    <link rel="stylesheet" href="css/style.css" />
    <title>Пример простого квиза</title>
  </head>
  <body>
    <div class="container">
      <div class="quiz">
        <!-- Форма, которая содержит весь квиз -->
        <form action="#!" class="quiz__form">
          <!-- Поле для каждого шага квиза -->
          <fieldset class="quiz__fieldset">
            <legend class="quiz__legend">
              <img src="./images/ava.webp" alt="avatar" class="quiz__ava" />
              <div class="quiz__question">
                <p>Оператор</p>
                <!-- Здесь будет отображаться текст вопроса -->
                <p id="question-text"></p>
              </div>
            </legend>
            <!-- Прогресс бар -->
            <div class="quiz__progress">
              <div class="quiz__progress-line">
                <div id="progress-fill" class="quiz__progress-fill"></div>
              </div>
            </div>
            <!-- Контейнер для отображения ответов -->
            <div class="quiz__answer" id="answers-container">
              <!-- Ответы будут добавлены динамически через JavaScript -->
            </div>
          </fieldset>
          <!-- Кнопка для перехода к следующему шагу -->
          <button type="button" class="quiz__button" onclick="nextStep()">
            Далее
          </button>
        </form>
      </div>
    </div>

    <!-- Подключение файла JavaScript -->
    <script src="./js/script.js"></script>
  </body>
</html>

CSS

/* Стили для body, добавляют фоновое изображение */
body {
  background-image: url(../images/low-angle-office.webp);
  background-attachment: fixed;
  background-size: cover;
}

/* Основной контейнер квиза */
.quiz {
  width: 100%;
  max-width: 900px;
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1 0 auto;
  margin-left: auto;
  margin-right: auto;
  user-select: none;
  margin: 200px;
}

/* Стили для формы квиза */
.quiz__form {
  display: flex;
  flex-direction: column;
  width: 100%;
  background-color: #fff;
  padding: 50px;
  border-radius: 5px;
}

/* Поле для шага квиза */
.quiz__fieldset {
  margin-bottom: 50px;
}

/* Легенда с вопросом и аватаром */
.quiz__legend {
  display: flex;
  align-items: center;
  background-color: #f1f1f1;
  padding: 15px;
  border-radius: 5px;
  margin-bottom: 20px;
  width: 100%;
}

/* Стили для аватара */
.quiz__ava {
  width: 60px;
  margin-right: 20px;
}

/* Стили для вопроса */
.quiz__question p:first-child {
  text-transform: uppercase;
  font-weight: 500;
}

/* Контейнер для прогресс-бара */
.quiz__progress {
  width: 100%;
  height: 10px;
  margin-bottom: 20px;
}

/* Линия прогресса */
.quiz__progress-line {
  width: 100%;
  height: 10px;
  background-color: #ddd;
  border-radius: 5px;
}

/* Заполнение прогресс-бара */
.quiz__progress-fill {
  width: 20%;
  height: 10px;
  background: linear-gradient(45deg, #1fadff 0, #2684bb 100%);
  border-radius: 5px;
  transition: width 0.3s;
}

/* Сетка для ответов */
.quiz__answer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* Стили для ответа */
.quiz__answer-item {
  display: flex;
  align-items: center;
  border: 1px solid #bbb;
  padding: 10px;
  border-radius: 5px;
  cursor: pointer;
}

/* Круг для отметки ответа */
.quiz__answer-item-circle {
  width: 25px;
  height: 25px;
  border-radius: 5px;
  border: 1px solid #bbb;
  margin-right: 15px;
  position: relative;
}

/* Иконка "проверки" ответа */
.quiz__answer-item-circle:after {
  width: 20px;
  height: 22px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  content: url(../images/check.svg);
  opacity: 0;
  transition: opacity 0.3s;
}

/* Текст ответа */
.quiz__answer-item-text {
  line-height: 1.5;
}

/* Скрытие радиокнопок */
.quiz__answer-item input {
  display: none;
}

/* Стили для выбранного ответа */
.quiz__answer-item input:checked + .quiz__answer-item-circle {
  border: 1px solid #2684bb;
}

.quiz__answer-item input:checked + .quiz__answer-item-circle:after {
  opacity: 1;
}

/* Стили для кнопки перехода */
.quiz__button {
  width: 300px;
  font-weight: 700;
  color: #fff;
  padding: 10px 0;
  background: linear-gradient(45deg, #1fadff 0, #2684bb 100%);
  border-radius: 5px;
  margin: 0 auto;
  cursor: pointer;
  font-size: 17px;
  text-align: center;
}

Логика квиза

Само взаимодействие происходит на стороне JavaScript. Вся логика крутится вокруг массива formSteps с вопросами и ответами. Каждый шаг представлен объектом, где есть свой текст вопроса и возможные варианты ответов. Это позволяет легко добавлять новые вопросы или редактировать существующие.

Ключевая функция — renderStep() — отвечает за отрисовку текущего вопроса и вариантов ответов. Когда пользователь выбирает один из ответов и нажимает кнопку «Далее», происходит переход к следующему вопросу. Если пользователь не выбрал ответ, его уведомят, что необходимо выбрать один из вариантов.

JavaScript

// Данные о каждом шаге квиза, включая вопрос и возможные ответы
const formSteps = [
  {
    question: "Что для вас является самым важным при выборе недвижимости?",
    answers: [
      "Близость к работе или учебе",
      "Тихий район и хорошие соседи",
      "Развитая инфраструктура и удобства",
      "Инвестиционная привлекательность",
    ],
  },
  {
    question:
      "Какие дополнительные удобства вы бы хотели видеть в вашей новой недвижимости?",
    answers: [
      "Фитнес-центр или бассейн на территории",
      "Парковочные места для автомобилей",
      "Охраняемая территория и видеонаблюдение",
      "Зона для барбекю и отдыха",
    ],
  },
  {
    question: "Как вы планируете использовать новую недвижимость?",
    answers: [
      "Личное проживание",
      "Сдача в аренду",
      "Коммерческая деятельность",
      "Как дача для отдыха",
    ],
  },
  {
    question: "Какой вид вам хотелось бы видеть из окон?",
    answers: [
      "Панорама города",
      "Вид на парк или лес",
      "Вид на воду — реку или озеро",
      "Горы или природные ландшафты",
    ],
  },
  {
    question: "Что для вас важнее всего в плане ремонта и отделки?",
    answers: [
      "Максимальная готовность для заселения",
      "Возможность сделать ремонт по своему вкусу",
      "Экологически чистые и современные материалы",
      "Ретро-стиль или уникальный дизайн",
    ],
  },
  {
    question: "Какой этаж для вас предпочтителен?",
    answers: [
      "Первый этаж, ближе к выходу",
      "Средние этажи (2-5)",
      "Высокие этажи с видом",
      "Без разницы, главное — удобство",
    ],
  },
  {
    question: "Какая отделка вам ближе?",
    answers: [
      "Минималистичный стиль",
      "Классический интерьер",
      "Современный дизайн с акцентами",
      "Скандинавский стиль или лофт",
    ],
  },
  {
    question: "Насколько важна для вас экологичность района?",
    answers: [
      "Очень важна, хочу жить в зелёной зоне",
      "Не имеет значения",
      "Важно, но возможны компромиссы",
      "Интересует, если это в пределах города",
    ],
  },
  {
    question: "Какой доступ к транспорту вам необходим?",
    answers: [
      "Станция метро или остановка в шаговой доступности",
      "Необходима парковка для личного автомобиля",
      "Предпочитаю велодорожки и пешие маршруты",
      "Не принципиально, работаю удалённо",
    ],
  },
  {
    question: "Какие особенности дома или здания для вас важны?",
    answers: [
      "Новая постройка",
      "Историческое здание",
      "Дом с террасой или садом",
      "Комплекс с закрытой территорией",
    ],
  },
];

// Переменная для отслеживания текущего шага
let currentStep = 0;

// Массив для хранения выбранных ответов
let selectedAnswers = [];

// Функция для отображения текущего шага квиза
function renderStep() {
  const questionText = document.getElementById("question-text");
  const answersContainer = document.getElementById("answers-container");
  const progressFill = document.getElementById("progress-fill");

  // Отображаем текущий вопрос
  questionText.textContent = formSteps[currentStep].question;
  answersContainer.innerHTML = "";

  // Создаем элементы для каждого ответа
  formSteps[currentStep].answers.forEach((answer, index) => {
    const label = document.createElement("label");
    label.classList.add("quiz__answer-item");
    label.setAttribute("for", `answer-${index}`);

    const input = document.createElement("input");
    input.setAttribute("type", "radio");
    input.setAttribute("name", `question-${currentStep}`);
    input.setAttribute("id", `answer-${index}`);
    input.value = answer;

    const circle = document.createElement("div");
    circle.classList.add("quiz__answer-item-circle");

    const text = document.createElement("div");
    text.classList.add("quiz__answer-item-text");
    text.textContent = answer;

    label.appendChild(input);
    label.appendChild(circle);
    label.appendChild(text);
    answersContainer.appendChild(label);
  });

  // Обновляем прогресс-бар
  const progress = ((currentStep + 1) / formSteps.length) * 100;
  progressFill.style.width = progress + "%";
}

// Функция для перехода к следующему шагу
function nextStep() {
  const selectedAnswer = document.querySelector(
    `input[name="question-${currentStep}"]:checked`
  );

  if (selectedAnswer) {
    // Сохраняем выбранный ответ
    selectedAnswers.push({
      question: formSteps[currentStep].question,
      answer: selectedAnswer.value,
    });
  } else {
    alert("Пожалуйста, выберите ответ.");
    return;
  }

  // Переход к следующему шагу или завершение квиза
  if (currentStep < formSteps.length - 1) {
    currentStep++;
    renderStep();
  } else {
    const messageText = selectedAnswers
      .map(
        (step, index) =>
          `Вопрос ${index + 1}: ${step.question}\nОтвет: ${step.answer}`
      )
      .join("\n\n");
    sendResults(messageText);
    alert("Квиз завершён");
  }
}

// Функция для отправки результатов
function sendResults(data) {
  // Выводим данные в консоль для отладки
  console.log("Отправляем данные...");
  console.log(data);
  // Здесь можно добавить код для отправки данных на сервер
}

document.addEventListener("DOMContentLoaded", renderStep);

Важные моменты

  • Гибкость: Квиз легко масштабируется: можно добавить сколько угодно шагов, просто расширив массив вопросов.
  • Обратная связь: После завершения всех шагов пользователю выводятся результаты — он видит свои ответы на каждый вопрос.
  • Простота в изменении стилей: CSS здесь структурирован так, что можно легко менять цвета, шрифты, размеры элементов, подстраивая дизайн под конкретный проект.

Такой квиз может быть полезен в самых разных сферах: от маркетинговых опросов до помощи в подборе недвижимости или обучения. Важно помнить, что основной акцент здесь сделан на простоту и удобство для пользователя.