首页 > 解决方案 > JavaScript 分页中的测验

问题描述

我正在 JS 中创建一个测验,问题一次只需要显示一个。当我选择一个答案选项时,我会收到“正确”或“错误”的通知,然后它会自动转到下一个问题。

var questions = [
    {
        question: "1. question 1 here",
        answers: [
            "1a here",
            "1c here",
            "1d here"
        ],
        correctanswer: "1b here"
    },
    {
        question: "2. question 2 here",
        answers: [
            "2a here",
            "2b here",
            "2c here",
            "2d here"
        ],
        correctanswer: "2a here"
    },
    {
        question: "3. question 3 here",
        answers: [
            "3a here",
            "3b here",
            "3c here",
            "3d here"
        ],
        correctanswer: "3d here"
    },    
]

function startQuiz() {
    var output =[];
    var answers;
    for (var i=0; i < questions.length; i++) {
        answers =[];    
        for(letter in questions[i].answers) {
            answers.push(
                '<label>'
                    + '<input type="radio" name="question' +i+'"value="'+letter+'">'
                    + letter + ': ' + questions[i].answers[letter] +
                '</label'
                // var userAns = document.getElementById("quiz")    
            // userAns.innerHTML = questions[i].question + "<br>" + questions[i].answers;
            );
        }
        output.push(
            '<div class="question">' + questions[i].question + '</div>'+
            'div class="answers">' + answers.join('') + '</div>'
        );
        // //if right or wrong
        // if (userAns === questions.correctanswer) {
        //     var grade = document.createElement("p");
        //     grade.textContent("Correct!");
        //     grade.className("grade");
        //     grade.setAttribute("grade-id");
        // } else {
        //     grade.textContent("Wrong!");
        //     timerRef - sec *10;
        // }
    }
    quizContainer.innerHTML = output.join('');
}

startButton.addEventListener('click', startTimer);
startButton.addEventListener('click', startQuiz);

我注释掉了一些行,因为我不确定如何让它们工作。我尝试制作答案选择单选按钮 - 只有在选择“开始”时才会显示问题。

JS 小提琴测验

标签: javascripthtmlcss

解决方案


你想要什么需要一些计划,我建议你把它分解成组件/脚本,这样你就可以轻松地将更改应用到你的代码中。

我写了一个简单的演示来说明我的观点。

var questions = [
    {
        question: "1. question 1 here",
        answers: [
            "1a here",
            "1b here",
            "1c here",
            "1d here"
        ],
        correctanswer: "1b here"
    },
    {
        question: "2. question 2 here",
        answers: [
            "2a here",
            "2b here",
            "2c here",
            "2d here"
        ],
        correctanswer: "2a here"
    },
    {
        question: "3. question 3 here",
        answers: [
            "3a here",
            "3b here",
            "3c here",
            "3d here"
        ],
        correctanswer: "3d here"
    },    
];

function Timer(maxMinutes) {
  var root = null;
  var display = null;
  var time = {
    minutes: maxMinutes,
    seconds: 0
  };
  var UPDATE_RATE = 1000;
  var jobId = null;
  var timeoutCallback = null;
  
  function formatTime() {
    var min = time.minutes;
    var sec = time.seconds;

    if (sec < 10) {
      sec = '0' + sec;
    }
    
    return min + ':' + sec;
  }
  
  function init() {
    root = document.createElement('div');
    root.classList.add('quiz__timer');
    
    display = document.createElement('span');
    display.classList.add('quiz__timer__display');
    display.innerText = formatTime();
    root.appendChild(display)
  }
  
  function update() {
    time.seconds = time.seconds - 1;
    
    if (time.seconds < 0) {
      time.minutes = time.minutes - 1;
      time.seconds = 59;
    }
     
    display.innerText = formatTime();
    
    if (time.minutes === 0 && time.seconds === 0) {
      stop();
      timeoutCallback();
    }
  }
  
  function mount(node) {
    node.appendChild(root);
  }
  
  function onTimeout(callback) {
    timeoutCallback = callback;
  }
  
  function unmount() {
    stop();
    root.remove();
  }
  
  function start() {
    jobId = window.setInterval(update, UPDATE_RATE);
  }
  
  function stop() {
    if (jobId !== null) {
      window.clearInterval(jobId);
      jobId = null;
    }
  }
  
  function getCurrentTime() {
    return display.innerText;
  }
  
  init();
  
  return {
    mount: mount,
    unmount: unmount,
    start: start,
    stop: stop,
    onTimeout: onTimeout,
    getCurrentTime: getCurrentTime
  }
}

function Question(data) {
  var root = null;
  var options = null;
  var validator = null;
  
  function init() {
    root = document.createElement('div');
    options = document.createElement('ul');
    
    var question = document.createElement('p');
    question.innerText = data.question;
    
    data.answers.forEach((opt) => {
      var option = document.createElement('li');
      option.dataset.value = opt;
      option.innerText = opt;
      options.appendChild(option);
    });
    
    root.classList.add('quiz__question');
    options.classList.add('quiz__question__options');
    question.classList.add('quiz__question__title');
    root.appendChild(question);
    root.appendChild(options);
  }
  
  function createValidator(callback) {
    return function (ev) {
      var answered = ev.target.dataset.value === data.correctanswer;
      if (answered) {
        ev.target.classList.add('correct');
      } else {
        ev.target.classList.add('incorrect');
      }
      options.classList.add('readonly');
      callback({
        question: data.question,
        correct: answered
      });
    }
  }
  
  function onAnswer(callback) {
    validator = createValidator(callback);
    options.addEventListener('click', validator);
  }
  
  function mount(node) {
    node.appendChild(root);
  }
  
  function unmount() {
    options.removeEventListener('click', validator);
    root.remove();
  }
  
  init();
  
  return {
    mount: mount,
    unmount: unmount,
    onAnswer: onAnswer
  }
}

function Quiz(data, mountingNode, onFinish, nextQuestionRate) {
  nextQuestionRate = nextQuestionRate || 2500;
  var started = false;
  var index = 0;
  var currentQuestion = null;
  var result = {
    remainingTime: '',
    answers: []
  };
  var timer = Timer(5);
 
  timer.onTimeout(notifyResult);
  
  function render() {
    timer.mount(mountingNode);
  }
  
  function notifyResult() {
    result.remainingTime = timer.getCurrentTime();
    onFinish(result)
  }
  
  function renderQuestionByIndex(currentIndex) {
    currentQuestion = Question(data[currentIndex]);
    currentQuestion.mount(mountingNode);
    currentQuestion.onAnswer(nextQuestion);
  }
  
  function nextQuestion(answer) {
    result.answers.push(answer);
    index += 1;    
    
    // queue next question
    if (index < data.length) {
      setTimeout(function () {
        currentQuestion.unmount();
        renderQuestionByIndex(index);
      }, 2500);
    } else {
      notifyResult();
      timer.stop();
    }
  }
  
  function start() {
    timer.start();
    renderQuestionByIndex(index);
  }
  
  render();
  
  return {
    start: start
  }
}



var quiz = Quiz(
  questions, 
  document.querySelector('.quiz'),
  function(result) {
    console.log(result);
  }
);

var start = document.querySelector('.quiz__start-button');

start.addEventListener('click', function(){
  quiz.start();
  start.disabled = true;
});
.quiz__timer {
  position: absolute;
  top: 0;
  right: 0;
}

.quiz {
  position: relative;
  min-height: 200px;
}

.quiz__question__title {
  margin: 0;
  font-weight: 600;
  margin-bottom: 10px;
}

.quiz__question__options {
  list-style: none;
  margin: 0;
  padding: 0;
}

.readonly {
  pointer-events: none;
}

.quiz__question__options li {
  cursor: pointer;
  padding: 10px;
}

.quiz__question__options li.correct {
  background-color: #9ae6b4;
}

.quiz__question__options li.incorrect {
  background-color: #feb2b2;
}

.quiz__question__options li:hover {
  background: #e2e8f0;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div class="quiz">

  </div>
  <button class="quiz__start-button">start</button>
</body>
</html>

如上所示,您会发现 3 个组件

  • 计时器更新时间并在用户时间用完时通知。
  • 问题验证用户回答并在回答时通知。
  • 测验计算问题答案,更改当前问题并通知结果。

推荐阅读