document.addEventListener('DOMContentLoaded', function () {
// Элементы DOM
const startScreen = document.getElementById('start-screen');
const quizScreen = document.getElementById('quiz-screen');
const resultScreen = document.getElementById('result-screen');
const reviewScreen = document.getElementById('review-screen');
const startBtn = document.getElementById('start-btn');
const prevBtn = document.getElementById('prev-btn');
const checkBtn = document.getElementById('check-btn');
const nextBtn = document.getElementById('next-btn');
const submitBtn = document.getElementById('submit-btn');
const restartBtn = document.getElementById('restart-btn');
const reviewBtn = document.getElementById('review-btn');
const backToResultsBtn = document.getElementById('back-to-results-btn');
const questionElement = document.getElementById('question');
const optionsContainer = document.getElementById('options-container');
const selectionInfo = document.getElementById('selection-info');
const selectedCountElement = document.getElementById('selected-count');
const totalOptionsElement = document.getElementById('total-options');
const feedbackContainer = document.getElementById('feedback-container');
const correctFeedback = document.getElementById('correct-feedback');
const partialFeedback = document.getElementById('partially-feedback');
const incorrectFeedback = document.getElementById('incorrect-feedback');
const partialCorrectCount = document.getElementById('partial-correct-count');
const totalCorrectCount = document.getElementById('total-correct-count');
const correctAnswersText = document.getElementById('correct-answers-text');
const progressBar = document.querySelector('.progress-bar');
const progressText = document.getElementById('progress-text');
const scoreElement = document.getElementById('score');
const percentageElement = document.getElementById('percentage');
const correctCountElement = document.getElementById('correct-count');
const partialCountElement = document.getElementById('partial-count');
const incorrectCountElement = document.getElementById('incorrect-count');
const reviewContainer = document.getElementById('review-container');
// Переменные состояния
let questions = [];
let shuffledQuestions = [];
let currentQuestionIndex = 0;
let userAnswers = []; // Массив массивов выбранных индексов
let questionStatus = []; // Статус ответа на каждый вопрос: 'unanswered', 'correct', 'partial', 'incorrect'
let score = 0;
let letters = ['а', 'б', 'в', 'г', 'д', 'е'];
// Загрузка вопросов из JSON
async function loadQuestions() {
try {
const response = await fetch('questions.json');
if (!response.ok) {
throw new Error('Не удалось загрузить вопросы');
}
questions = await response.json();
console.log('Вопросы загружены:', questions.length);
} catch (error) {
console.error('Ошибка загрузки вопросов:', error);
createDemoQuestions();
}
}
// Создание демо-вопросов, если файл не найден
function createDemoQuestions() {
questions = [
{
"text": "Составляющие мультимедиа могут быть разбиты на основные группы?",
"options": [
{ "text": "Текстовая, визуальная и звуковая информация.", "correct": false },
{ "text": "Текстовая, визуальная, звуковая информация и данные.", "correct": true },
{ "text": "Текстовая, визуальная, звуковая информация, данные и графическая информация.", "correct": false },
{ "text": "Только текстовая и визуальная информация.", "correct": false }
]
},
{
"text": "С точки зрения передачи мультимедиа могут быть классифицированы на?",
"options": [
{ "text": "Передаваемые в реальном времени.", "correct": true },
{ "text": "Передаваемые в on-line.", "correct": false },
{ "text": "Передаваемые в не реальном времени.", "correct": true },
{ "text": "Только передаваемые по запросу.", "correct": false }
]
}
];
console.log('Созданы демо-вопросы с множественным выбором');
}
// Инициализация теста
function initializeQuiz() {
// Перемешиваем вопросы
shuffledQuestions = [...questions];
shuffleArray(shuffledQuestions);
// Перемешиваем варианты ответов в каждом вопросе
shuffledQuestions.forEach(question => {
shuffleArray(question.options);
// Находим индексы правильных ответов после перемешивания
question.correctOptionIndices = [];
question.options.forEach((option, index) => {
if (option.correct) {
question.correctOptionIndices.push(index);
}
});
// Сохраняем текст правильных ответов для отображения
question.correctAnswersText = question.correctOptionIndices
.map(idx => `${letters[idx]}) ${question.options[idx].text}`)
.join('; ');
});
// Инициализируем массивы
userAnswers = new Array(shuffledQuestions.length).fill(null).map(() => []);
questionStatus = new Array(shuffledQuestions.length).fill('unanswered');
// Сбрасываем индекс текущего вопроса и счет
currentQuestionIndex = 0;
score = 0;
// Обновляем UI
updateProgress();
showQuestion();
}
// Перемешивание массива (алгоритм Фишера-Йетса)
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// Показать вопрос
function showQuestion() {
const question = shuffledQuestions[currentQuestionIndex];
questionElement.textContent = `${currentQuestionIndex + 1}. ${question.text}`;
// Обновляем информацию о выборе
totalOptionsElement.textContent = question.options.length;
updateSelectionInfo();
// Скрываем блок обратной связи, если вопрос еще не проверен
if (questionStatus[currentQuestionIndex] === 'unanswered') {
feedbackContainer.classList.add('hidden');
} else {
showFeedback();
}
// Очищаем контейнер с вариантами
optionsContainer.innerHTML = '';
// Создаем варианты ответов
question.options.forEach((option, index) => {
const optionElement = document.createElement('div');
optionElement.className = 'option';
// Проверяем, выбран ли этот вариант
const isSelected = userAnswers[currentQuestionIndex].includes(index);
const isChecked = questionStatus[currentQuestionIndex] !== 'unanswered';
if (isSelected) {
optionElement.classList.add('selected');
}
// Если вопрос проверен, показываем правильность ответов
if (isChecked) {
optionElement.classList.add('checked');
const isCorrectOption = question.correctOptionIndices.includes(index);
if (isSelected && isCorrectOption) {
optionElement.classList.add('correct');
} else if (isSelected && !isCorrectOption) {
optionElement.classList.add('incorrect');
} else if (!isSelected && isCorrectOption) {
optionElement.classList.add('correct');
}
}
optionElement.innerHTML = `
${letters[index]}) ${option.text}
`;
// Если вопрос еще не проверен, добавляем обработчик клика
if (!isChecked) {
optionElement.addEventListener('click', () => toggleOption(index));
}
optionsContainer.appendChild(optionElement);
});
// Обновляем состояние кнопок навигации
updateNavigationButtons();
}
// Переключить выбор варианта
function toggleOption(optionIndex) {
const currentAnswers = userAnswers[currentQuestionIndex];
const index = currentAnswers.indexOf(optionIndex);
if (index === -1) {
// Добавляем вариант
currentAnswers.push(optionIndex);
} else {
// Удаляем вариант
currentAnswers.splice(index, 1);
}
// Обновляем отображение
updateSelectionInfo();
showQuestion();
}
// Обновить информацию о выбранных вариантах
function updateSelectionInfo() {
const selectedCount = userAnswers[currentQuestionIndex].length;
selectedCountElement.textContent = selectedCount;
// Подсвечиваем информацию в зависимости от количества выбранных ответов
if (selectedCount === 0) {
selectionInfo.style.borderColor = '#e0e0e0';
selectionInfo.style.backgroundColor = '#f0f4ff';
} else {
selectionInfo.style.borderColor = '#4a00e0';
selectionInfo.style.backgroundColor = '#e6f7ff';
}
}
// Обновить кнопки навигации
function updateNavigationButtons() {
const isAnswered = questionStatus[currentQuestionIndex] !== 'unanswered';
const hasSelection = userAnswers[currentQuestionIndex].length > 0;
prevBtn.disabled = currentQuestionIndex === 0;
if (isAnswered) {
checkBtn.classList.add('hidden');
nextBtn.classList.remove('hidden');
} else {
checkBtn.classList.remove('hidden');
nextBtn.classList.add('hidden');
checkBtn.disabled = !hasSelection;
}
// Показываем кнопку завершения на последнем проверенном вопросе
const allQuestionsAnswered = questionStatus.every(status => status !== 'unanswered');
const isLastQuestion = currentQuestionIndex === shuffledQuestions.length - 1;
if (isLastQuestion && isAnswered) {
nextBtn.classList.add('hidden');
submitBtn.classList.remove('hidden');
} else {
submitBtn.classList.add('hidden');
}
}
// Проверить ответ и показать результат
function checkAnswer() {
const question = shuffledQuestions[currentQuestionIndex];
const userSelected = userAnswers[currentQuestionIndex];
const correctIndices = question.correctOptionIndices;
// Находим количество правильных выбранных ответов
const correctSelected = userSelected.filter(idx => correctIndices.includes(idx)).length;
const incorrectSelected = userSelected.length - correctSelected;
const missedCorrect = correctIndices.length - correctSelected;
// Определяем статус ответа
let status;
let feedbackType;
if (incorrectSelected === 0 && missedCorrect === 0) {
// Все правильные выбраны, ничего лишнего
status = 'correct';
feedbackType = 'correct';
score += 1;
} else if (correctSelected > 0 && (incorrectSelected > 0 || missedCorrect > 0)) {
// Частично правильно
status = 'partial';
feedbackType = 'partial';
score += 0.5; // Половина балла за частично правильный ответ
} else {
// Полностью неправильно
status = 'incorrect';
feedbackType = 'incorrect';
}
// Сохраняем статус вопроса
questionStatus[currentQuestionIndex] = status;
// Показываем обратную связь
showFeedback(correctSelected, correctIndices.length);
// Обновляем навигацию
updateNavigationButtons();
// Обновляем отображение вариантов
showQuestion();
}
// Показать обратную связь
function showFeedback(correctSelected = 0, totalCorrect = 0) {
const question = shuffledQuestions[currentQuestionIndex];
const status = questionStatus[currentQuestionIndex];
// Скрываем все виды обратной связи
correctFeedback.classList.add('hidden');
partialFeedback.classList.add('hidden');
incorrectFeedback.classList.add('hidden');
// Показываем соответствующий вид обратной связи
switch (status) {
case 'correct':
correctFeedback.classList.remove('hidden');
break;
case 'partial':
partialFeedback.classList.remove('hidden');
partialCorrectCount.textContent = correctSelected;
totalCorrectCount.textContent = totalCorrect;
break;
case 'incorrect':
incorrectFeedback.classList.remove('hidden');
correctAnswersText.textContent = question.correctAnswersText;
break;
}
feedbackContainer.classList.remove('hidden');
}
// Обновление прогресса
function updateProgress() {
const progress = ((currentQuestionIndex + 1) / shuffledQuestions.length) * 100;
progressBar.style.setProperty('--width', `${progress}%`);
progressText.textContent = `Вопрос ${currentQuestionIndex + 1} из ${shuffledQuestions.length}`;
}
// Переход к следующему вопросу
function nextQuestion() {
if (currentQuestionIndex < shuffledQuestions.length - 1) {
currentQuestionIndex++;
updateProgress();
showQuestion();
}
}
// Переход к предыдущему вопросу
function prevQuestion() {
if (currentQuestionIndex > 0) {
currentQuestionIndex--;
updateProgress();
showQuestion();
}
}
// Завершение теста и подсчет результатов
function finishQuiz() {
// Подсчет детальной статистики
let correctCount = 0;
let partialCount = 0;
let incorrectCount = 0;
questionStatus.forEach(status => {
switch (status) {
case 'correct':
correctCount++;
break;
case 'partial':
partialCount++;
break;
case 'incorrect':
incorrectCount++;
break;
}
});
// Обновление экрана результатов
scoreElement.textContent = Math.round(score * 10) / 10; // Округляем до одного знака после запятой
const percentage = Math.round((score / shuffledQuestions.length) * 100);
percentageElement.textContent = `${percentage}%`;
correctCountElement.textContent = correctCount;
partialCountElement.textContent = partialCount;
incorrectCountElement.textContent = incorrectCount;
// Обновление кругового индикатора
const circle = document.querySelector('.circle');
circle.style.background = `conic-gradient(#4a00e0 ${percentage}%, #e0e0e0 ${percentage}%)`;
// Переключение экранов
quizScreen.classList.add('hidden');
resultScreen.classList.remove('hidden');
}
// Показать обзор ответов
function showReview() {
reviewContainer.innerHTML = '';
shuffledQuestions.forEach((question, index) => {
const reviewItem = document.createElement('div');
reviewItem.className = 'review-item';
const status = questionStatus[index];
const userSelected = userAnswers[index];
const correctIndices = question.correctOptionIndices;
// Определяем статус для отображения
let statusText, statusClass;
switch (status) {
case 'correct':
statusText = 'Правильно';
statusClass = 'status-correct';
break;
case 'partial':
statusText = 'Частично правильно';
statusClass = 'status-partial';
break;
case 'incorrect':
statusText = 'Неправильно';
statusClass = 'status-incorrect';
break;
default:
statusText = 'Не отвечено';
statusClass = 'status-incorrect';
}
let optionsHTML = '';
question.options.forEach((option, optionIndex) => {
let optionClass = '';
const isCorrect = correctIndices.includes(optionIndex);
const isSelected = userSelected.includes(optionIndex);
if (isCorrect && isSelected) {
optionClass = 'user-correct';
} else if (isCorrect && !isSelected) {
optionClass = 'correct-answer';
} else if (!isCorrect && isSelected) {
optionClass = 'user-incorrect';
} else if (status === 'partial' && isSelected && !isCorrect) {
optionClass = 'user-partial';
}
optionsHTML += `
${letters[optionIndex]}) ${option.text}
`;
});
reviewItem.innerHTML = `
${index + 1}. ${question.text}
${statusText}
${optionsHTML}
Ваши ответы: ${userSelected.length > 0
? userSelected.map(idx => letters[idx]).join(', ')
: 'нет ответа'}
`;
reviewContainer.appendChild(reviewItem);
});
resultScreen.classList.add('hidden');
reviewScreen.classList.remove('hidden');
}
// Начало теста
function startQuiz() {
startScreen.classList.add('hidden');
quizScreen.classList.remove('hidden');
initializeQuiz();
}
// События
startBtn.addEventListener('click', startQuiz);
prevBtn.addEventListener('click', prevQuestion);
checkBtn.addEventListener('click', checkAnswer);
nextBtn.addEventListener('click', nextQuestion);
submitBtn.addEventListener('click', finishQuiz);
restartBtn.addEventListener('click', () => {
resultScreen.classList.add('hidden');
startScreen.classList.remove('hidden');
});
reviewBtn.addEventListener('click', showReview);
backToResultsBtn.addEventListener('click', () => {
reviewScreen.classList.add('hidden');
resultScreen.classList.remove('hidden');
});
// Загружаем вопросы при загрузке страницы
loadQuestions();
});