Здравствуйте, программисты! Сегодня в этом блоге вы узнаете, как создать приложение-викторину с таймером с множественным выбором, используя код HTML CSS и JavaScript.

Моя цель — провести вас от планирования до создания приложения-викторины с таймером. Для этого мы будем использовать ванильный JavaScript, CSS и HTML. Никаких дополнительных библиотек или пакетов. Давайте начнем с определения того, что может делать наше приложение Quiz.

Приложение Quiz будет разделено на два основных класса. Первая будет областью настроек, в которой игрок может выбрать сложность, категорию и количество вопросов, на которые он хочет ответить. Для этого мы создадим класс настроек для отслеживания всей этой информации. После этого он может начать викторину.

Второй областью будет викторина. quiz-class отслеживает прогресс игрока и решает, отображать ли следующий вопрос последнего экрана.

Кроме того, класс-викторина имеет два других компонента, первый из массива классов-вопросов, которые содержат данные вопроса, отображают его и проверяют, был ли ответ правильно или нет. Другой — final-class, который отображает последнюю страницу со счетом игрока.

Мы будем использовать API Open Trivia DB для вопросов, чтобы нам не приходилось придумывать свои вопросы.

Викторина с несколькими вариантами ответов с HTML-кодом таймера: -

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vanilla Quiz</title>
    <link rel="stylesheet" href="styles.css" />
    <link
      href="https://fonts.googleapis.com/css2?family=Questrial&display=swap"
      rel="stylesheet"
    />
  </head>
  <body>
    <main>
      <div class="header">
        <h2>Vanilla Quiz</h2>
      </div>
      <div class="main">
        <div class="final">
          <h3>You answered all of the questions!</h3>
          <p>Score:</p>
          <p class="score"></p>
          <h4>Want to try it again?</h4>
          <button id="again" class="submit">Again</button>
        </div>
        <div class="quiz">
          <div class="count">
            <p class="current">0</p>
            <p style="margin-left: 40px">/</p>
            <p class="total"></p>
          </div>
          <h3 id="question"></h3>
          <label id="a1" class="container">
            <input type="radio" checked="checked" name="radio" />
            <span class="checkmark"></span>
          </label>
          <label id="a2" class="container">
            <input type="radio" name="radio" />
            <span class="checkmark"></span>
          </label>
          <label id="a3" class="container">
            <input type="radio" name="radio" />
            <span class="checkmark"></span>
          </label>
          <label id="a4" class="container">
            <input type="radio" name="radio" />
            <span class="checkmark"></span>
          </label>
          <button id="next" class="submit">Submit</button>
        </div>
        <div class="settings">
          <h3 style="text-align: center">Set up your Quiz!</h3>
          <label for="category">Category</label>
          <select name="category" id="category">
            <option value="9">General Knowledge</option>
            <option value="27">Animals</option>
            <option value="15">Video Games</option>
            <option value="23">History</option>
            <option value="21">Sports</option>
          </select>
          <div class="mt30">
            <label for="difficulty">Difficulty</label>
            <label class="container" style="display: inline; margin-left: 30px"
              >Easy
              <input type="radio" name="radio" id="easy" />
              <span class="checkmark" style="margin-top: 2px"></span>
            </label>
            <label class="container" style="display: inline; margin-left: 30px"
              >Medium
              <input type="radio" name="radio" id="medium" />
              <span class="checkmark" style="margin-top: 2px"></span>
            </label>
            <label class="container" style="display: inline; margin-left: 30px"
              >Hard
              <input type="radio" name="radio" id="hard" />
              <span class="checkmark" style="margin-top: 2px"></span>
            </label>
          </div>
          <div class="mt30">
            <label for="questions">Number of questions</label>
            <input
              name="questions"
              id="questions"
              type="text"
              pattern="[0-9]*"
            />
          </div>
          <button id="start" class="submit">Start</button>
        </div>
      </div>
    </main>
    <script type="module" src="index.js"></script>
  </body>
</html>

Код начинается с тега, который содержит метатеги, определяющие заголовок страницы и автора. Следующая строка — это ссылка на файл styles.css, определяющий внешний вид сайта. Затем код начинается с объявления типа документа HTML5: это сообщает браузерам, что этот документ написан на HTML5, а не на более старых версиях HTML, таких как XHTML, или даже на языках разметки на основе XML, таких как SVG или MathML.

Он также объявляет, какую кодировку символов следует использовать для отображения этого документа (UTF-8). Это важно, потому что разные страны используют разные кодировки при просмотре веб-сайтов на своих компьютерах; UTF-8 позволяет всем пользователям просматривать веб-страницы без необходимости что-либо менять в настройках своего компьютера.

Затем идет метатег name=”viewport”, который устанавливает, насколько широким будет отображаться окно браузера при его начальном масштабе (1), прежде чем оно будет масштабироваться в зависимости от ширины устройства, как только оно загрузит больше контента в свою буферную зону (device-width ). Это не позволяет мобильным устройствам отображать слишком мелкий текст, в то время как настольные компьютеры по умолчанию отображают слишком большой текст, поскольку их экраны больше, чем у мобильных устройств.

Вывод HTML-кода:-

скопируйте и вставьте код CSS, потому что он не требует пояснений

Код CSS для викторины с множественным выбором: -

:root {
  --primary-color: #5D737E;
  --secondary-color: #D6F8D6;
  --tertiary-color: #7FC6A4;
  --quaternary-color: #55505C;
  --hover-color: #4e616b;
  --shadow-color:rgba(57, 127, 93, 0.4);
  --font-style: 'Questrial';
}
body {
  font-family: var(--font-style), 'Ranchers', cursive;
  background-color: var(--secondary-color);
  width: 100vw;
  height: 100vh;
  justify-content: center;
  align-items: center;
}
h2 {
  font-size: 3.5rem;
  text-align: center;
  color: var(--primary-color);
}
.mt30 {
  margin-top: 30px;
}
.header {
  padding: 15px;
}
.main {
  display: flex;
  justify-content: center;
}
.settings {
  z-index: 1;
}
.final {
  visibility: hidden;
  z-index: 2;
}
.final p {
  font-size: 30px;
  text-align: center;
}
.final h4 {
  font-size: 33px;
  text-align: center;
}
.quiz  {
  visibility: hidden;
  z-index: 0;
}
#questions {
  font-size: 20px;
  font-family: var(--font-style), 'Ranchers', cursive;
  font-weight: 600;
  line-height: 1.3;
  color: white;
  background-color: var(--primary-color);
  appearance: none;
  border: none;
  padding: 5px;
  border-radius: 5px;
  margin-left: 30px;
  outline: none;
  text-align: center;
  width: 120px;
}
.settings select {
  font-size: 20px;
  font-family: var(--font-style), 'Ranchers', cursive;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 1px;
  color: white;
  background-color: var(--primary-color);
  -moz-appearance: none;
  -webkit-appearance: none;
  appearance: none;
  border: none;
  padding: 5px;
  border-radius: 5px;
  margin-left: 20px;
  outline: none;
  text-align: center;
}
.settings select::-ms-expand {
  display: none;
}
.settings select:hover {
  border-color: var(--hover-color);
}
.settings select:focus {
  border-color: var(--hover-color);
}
.settings select option {
  /* font-weight: bolder; */
  font-family: var(--font-style), 'Ranchers', sans-serif;
}
.settings label {
  font-size: 25px;
  margin-right: 16px;
}

.quiz, .settings, .final {
  position: absolute;
  padding: 0px 35px 35px 35px;
  max-width: 560px;
  background-color: var(--tertiary-color);
  border-radius: 7px;
  -webkit-box-shadow: 10px 10px 3px -4px var(--shadow-color);
  -moz-box-shadow: 10px 10px 3px -4px var(--shadow-color);
  box-shadow: 10px 10px 5px -4px var(--shadow-color);
}
h3 {
  display: block;
  width: 550px;
  font-size: 35px;
  font-weight: 350;
  word-wrap: break-word;
}
.submit {
  width: 100%;
  color: white;
  background-color: var(--primary-color);
  font-family: var(--font-style), 'Ranchers', cursive;
  outline: none;
  border: none;
  height: 50px;
  font-size: 1.8rem;
  margin-top: 20px;
  border-radius: 5px;
  letter-spacing: 2px;
}
.submit:hover {
  background-color: var(--hover-color);
  cursor: pointer;
  color: #FAF33E;
}
/* The container */
.count {
  display: block;
  left: 75%;
  position: relative;
  padding-left: 35px;
  margin-bottom: 100px;
  cursor: pointer;
}
.count p {
  position: absolute;
  font-size: 35px;
  
}
.total {
  margin-left: 50px;
}
/* The container */
.container {
  display: block;
  position: relative;
  padding-left: 35px;
  margin-bottom: 12px;
  cursor: pointer;
  font-size: 25px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
/* Hide the browser's default radio button */
.container input {
  position: absolute;
  opacity: 0;
  cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
  position: absolute;
  top: -2px;
  left: 0px;
  height: 25px;
  width: 25px;
  background-color: white;
  border-radius: 30%;
}
/* On mouse-over, add a grey background color */
.container:hover input ~ .checkmark {
  background-color: #FAF33E;
}
/* When the radio button is checked, add a blue background */
.container input:checked ~ .checkmark {
  background-color: var(--quaternary-color);
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
  content: "";
  position: absolute;
  display: none;
}
/* Show the indicator (dot/circle) when checked */
.container input:checked ~ .checkmark:after {
  display: block;
}

это весь код Css, который нам нужен для оформления приложения-викторины.

Вывод кода Html + Css: -

Код JavaScript для викторины с множественным выбором: -

вопрос.js

class Question {
  constructor(question) {
    this.questionElement = document.querySelector('#question');
    this.answerElements = [
      document.querySelector('#a1'),
      document.querySelector('#a2'),
      document.querySelector('#a3'),
      document.querySelector('#a4'),
    ];
    this.correctAnswer = question.correct_answer;
    this.question = question.question;
    this.isCorrect = false;
    this.answers = this.shuffleAnswers([
      question.correct_answer, 
      ...question.incorrect_answers
    ]);
  }
  shuffleAnswers(answers) {
    for (let i = answers.length - 1; i > 0; i--){
      const j = Math.floor(Math.random() * i)
      const temp = answers[i]
      answers[i] = answers[j]
      answers[j] = temp
    }
    return answers;
  }
  answer(checkedElement) {
     this.isCorrect = (checkedElement[0].textContent === this.correctAnswer) ? true : false;
  }
  render() {
    this.questionElement.innerHTML = this.question;
    this.answerElements.forEach((el, index) => {
      el.innerHTML = '<input type="radio" name="radio"><span class="checkmark"></span>' + this.answers[index];
    });
  }
}
export default Question;

final.js

class Final {
  constructor(count, totalAmount) {
    this.scoreElement = document.querySelector('.score');
    this.againButton = document.querySelector('#again');

    this.render(count, totalAmount);
    this.againButton.addEventListener('click', location.reload.bind(location));
  }

  render(count, totalAmount) {
    this.scoreElement.innerHTML = `You answered ${count} out of ${totalAmount} correct!`;
  }
}

export default Final;

quiz.js

import Final from './final.js';
import Question from './question.js'
class Quiz {
  constructor(quizElement, amount, questions) {
    this.quizElement = quizElement;
    this.currentElement = document.querySelector('.current');
    this.totalElement = document.querySelector('.total');
    this.nextButton = document.querySelector('#next');
    this.finalElement = document.querySelector('.final')
    this.totalAmount = amount;
    this.answeredAmount = 0;
    this.questions = this.setQuestions(questions);
    
    this.nextButton.addEventListener('click', this.nextQuestion.bind(this));
    this.renderQuestion();
  }
  setQuestions(questions) {
    return questions.map(question => new Question(question));
  }
  renderQuestion() {
    this.questions[this.answeredAmount].render();
    this.currentElement.innerHTML = this.answeredAmount;
    this.totalElement.innerHTML = this.totalAmount;
  }
  nextQuestion() {
    const checkedElement = this.questions[this.answeredAmount].answerElements.filter(el => el.firstChild.checked);
    if (checkedElement.length === 0) {
      alert('You need to select an answer');
    } else {
      this.questions[this.answeredAmount].answer(checkedElement)
      this.showResult();
      this.answeredAmount++;
      (this.answeredAmount < this.totalAmount) ? this.renderQuestion() : this.endQuiz();
    }
  }
  showResult() {
    this.questions[this.answeredAmount].isCorrect ? alert('Correct answer :)') : alert('Wrong answer :(');
  }
  endQuiz() {
    this.quizElement.style.visibility = 'hidden';
    this.finalElement.style.visibility = 'visible';
    const correctAnswersTotal = this.calculateCorrectAnswers();
    this.final = new Final(correctAnswersTotal, this.totalAmount);
  }
  calculateCorrectAnswers() {
    let count = 0;
    this.questions.forEach(el => {
      if (el.isCorrect) {
        count++;
      }
    });
    return count;
  }
}
export default Quiz;

settings.js

import Quiz from './quiz.js';
class Settings {
  constructor() {
    this.quizElement = document.querySelector('.quiz');
    this.settingsElement = document.querySelector('.settings');
    this.category = document.querySelector('#category');
    this.numberOfQuestions = document.querySelector('#questions');
    this.difficulty = [
      document.querySelector('#easy'),
      document.querySelector('#medium'),
      document.querySelector('#hard'),
    ];
    this.startButton = document.querySelector('#start');
    this.quiz = { };
    this.startButton.addEventListener('click', this.startQuiz.bind(this));
  }
  async startQuiz() {
    try {
      const amount = this.getAmount();
      const categoryId = this.category.value;
      const difficulty = this.getCurrentDifficulty();
      const url = `https://opentdb.com/api.php?amount=${amount}&category=${categoryId}&difficulty=${difficulty}&type=multiple`;
      let data = await this.fetchData(url);
      this.toggleVisibility();
      this.quiz = new Quiz(this.quizElement, amount, data.results);
    } catch (error) {
      alert(error);
    }
  }
  toggleVisibility() {
    this.settingsElement.style.visibility = 'hidden';
    this.quizElement.style.visibility = 'visible';
  }
  async fetchData(url) {
    const response = await fetch(url);
    const result = await response.json();
    return result;
  }
  getCurrentDifficulty() {
    const checkedDifficulty = this.difficulty.filter(element => element.checked);
    if (checkedDifficulty.length === 1) {
      return checkedDifficulty[0].id;
    } else {
      throw new Error('Please select a difficulty!');
    }
  }
  getAmount() {
    const amount = this.numberOfQuestions.value;
    // Not negative, not 0 and not over 50
    if (amount > 0 && amount < 51) {
      return amount;
    }
    throw new Error('Please enter a number of questions between 1 and 50!');
  }
}
export default Settings;

index.js

import Settings from './quiz/settings.js';
new Settings();

Что мы здесь делаем?

Во-первых, мы получаем все три значения, для количества и сложности мы используем методы, которые еще не реализованы. В этих методах мы будем обрабатывать ошибки, например. не выбирая какую-либо сложность или вводя отрицательное число для количества вопросов.

После этого мы создаем URL с только что полученными параметрами. Этот URL-адрес передается в метод fetchData-method, который отправляет запрос и возвращает данные. После этого мы вызываем toggleVisibility и инициализируем новый объект-викторины, передавая результат, количество и элемент quizElement.

Если в какой-то момент возникнет ошибка, мы перехватим ее и отобразим с помощью метода предупреждения.

Оба метода getAmount и getCurrentDifficulty возвращают ошибку, если игрок ничего не выбрал или выбранное значение выходит за допустимые пределы (для количества вопросов). Мы также добавили import-statement для quiz-class вверху этого файла. Два других метода (fetchData и toggleVisibility) делают именно то, что следует из их названий. Теперь мы можем сосредоточиться на следующем викторине.

На этот раз у нас есть несколько аргументов, переданных объектом settings-object, с которыми нам нужно разобраться. Для вопросов мы создаем один объект-вопроса для каждого вопроса, переданного объектом-настроек. конструктор нуждается в дополнительной настройке, поэтому мы добавим еще несколько DOM-элементов и прослушиватель событий в nextButton. Итак, давайте продолжим и сделаем это!

Следующим шагом является реализация методов shuffleAnswers, answer и render. Для перетасовки массива мы будем использовать алгоритм Fisher-Yates-Shuffle-Algorithm.

Метод ответа просто сравнит выбор игрока со свойством correctAnswer, а метод рендеринга отобразит вопрос и все возможные ответы. . Чтобы это работало, нам нужно получить соответствующие DOM-элементы и в итоге получить этот question.js:

Теперь не хватает только final-class. Этот класс действительно прост, нам просто нужно получить DOM-элементы для отображения финального результата игроку. Для большего удобства мы можем добавить кнопку «снова», которая перезагружает страницу, чтобы проигрыватель мог начать заново.

Приложение викторины готово. Мы реализовали это с помощью простого старого JavaScript и использовали концепцию объектно-ориентированного программирования. Надеюсь, вам понравилось, и, как всегда, вы можете найти код на моем GitHub.

Окончательный вывод викторины с таймером с использованием кода HTML и JavaScript: -

Вы завершили изучение Викторины с таймером, используя HTML, CSS и JavaScript.

Если вам понравилось читать этот пост и он оказался для вас полезным, поделитесь им с друзьями и подпишитесь на меня, чтобы получать обновления о моих будущих постах. Вы можете связаться со мной в Instagram.

если у вас есть какие-либо недоразумения, прокомментируйте ниже или вы можете связаться с нами, заполнив нашу контактную форму в домашнем разделе.

автор — Ninja_webTech

Какой редактор кода вы используете для этого приложения-викторины с кодированием по таймеру?

Я лично рекомендую использовать VS Code Studio, он прост и удобен в использовании.

Является ли этот проект адаптивным или нет?

Нет, это не адаптивный проект

Используете ли вы какие-либо внешние ссылки для создания этого проекта?

Нет, мы используем чистый код Html, Css и JavaScript для приложения Quiz.