阅读 1011

制作 JavaScript 测验表 demo

本文通过分段叙述JavaScript代码实现测验demo,此demo功能如下:

  • 可自定义问题及答案
  • 分页显示问题
  • 显示答对题目数目
  • 对于正确和错误答案就行区分
  • 倒计时功能

测验的基本结构

首先,罗列测验问题和答案在JavaScript代码中,然后脚本就会自动产生测验。因此就不需要许多重复的代码,可以很容易增加或删除问题。

HTML结构如下:

  • <div>放置问题
  • <button>提交测验答案
  • <div>显示结果
<div id="quiz"></div>
<button id="submit">Submit Quiz</button>
<div id="results"></div>复制代码

然后通过DOM操作引用这些元素。

const quizContainer = document.getElementById('quiz');
const resultsContainer = document.getElementById('results');
const submitButton = document.getElementById('submit');复制代码

接着,我们需要方法建立测验表,显示结果。

function buildQuiz(){}

function showResults(){}

// 显示测验表
buildQuiz();

// 监听提交事件,运行showResults方法
submitButton.addEventListener('click', showResults);复制代码

显示测验问题

我们需要将问题显示出来,所以采用迭代方法。就需要用数组来装载所有问题,而且用数组结构迭代更容易。

const myQuestions = [
  {
    question: "Who is the strongest?",
    answers: {
      a: "Superman",
      b: "The Terminator",
      c: "Waluigi, obviously"
    },
    correctAnswer: "c"
  },
  {
    question: "What is the best site ever created?",
    answers: {
      a: "SitePoint",
      b: "Simple Steps Code",
      c: "Trick question; they're both the best"
    },
    correctAnswer: "c"
  },
  {
    question: "Where is Waldo really?",
    answers: {
      a: "Antarctica",
      b: "Exploring the Pacific Ocean",
      c: "Sitting in a tree",
      d: "Minding his own business, so stop asking"
    },
    correctAnswer: "d"
  }
];复制代码

现在我们开始罗列问题,并将其显示到页面中。

function buildQuiz(){
  // output数组存放输出结果
  const output = [];

  // 问题迭代
  myQuestions.forEach(
    (currentQuestion, questionNumber) => {

      // answers数组存放问题的答案
      const answers = [];

      for(letter in currentQuestion.answers){

        // 添加单选按钮
        answers.push(
          `<label>
            <input type="radio" name="question${questionNumber}" value="${letter}">
            ${letter} :
            ${currentQuestion.answers[letter]}
          </label>`
        );
      }

      // 将问题和答案放入output数组中
      output.push(
        `<div class="question"> ${currentQuestion.question} </div>
        <div class="answers"> ${answers.join('')} </div>`
      );
    }
  );

  // 将output数组转换为string类型,输出到网页页面
  quizContainer.innerHTML = output.join('');
}复制代码

显示测验结果

使用showResults迭代问题答案,检查他们,并显示结果

function showResults(){

  // 获取所有answers类的元素
  const answerContainers = quizContainer.querySelectorAll('.answers');

  //记录正确个数
  let numCorrect = 0;

  // 迭代问题
  myQuestions.forEach( (currentQuestion, questionNumber) => {

    //获取所选的答案
    const answerContainer = answerContainers[questionNumber];
    const selector = 'input[name=question'+questionNumber+']:checked';
    const userAnswer = (answerContainer.querySelector(selector) || {}).value;

    //如果回答正确
    if(userAnswer===currentQuestion.correctAnswer){
      // 正确个数+1
      numCorrect++;

      //将正确答案变成绿色
      answerContainers[questionNumber].style.color = 'lightgreen';
    }
    //如果答案错误或者是没答
    else{
      // 颜色变成红色
      answerContainers[questionNumber].style.color = 'red';
    }
  });

  // 显示答对的题目数目
  resultsContainer.innerHTML = numCorrect + ' out of ' + myQuestions.length;
}复制代码

处理没答完情况

如果有题没答,使用.value就会造成异常,因为无法获取不存在的的.value。所以为了解决这个就需要使用或操作||

  • 获取选择元素的引用,如果不存在则使用空对象。
  • 获取其值

所以,就会获得用户答案或undefined

最后,可以使用IIFE包装起来,这样就可以立即被调用。这样。变量也不会暴露在全局范围内,而且也不会影响别的函数块。

(function(){
  // put the rest of your code here
})();复制代码

增加页码

想让一页只显示一个问题。所以需要:

  • 显示和隐藏问题方式
  • 操作测验表的按钮

所以需要升级HTML结构如下:

<div class="quiz-container">
  <div id="quiz"></div>
</div>
<button id="previous">Previous Question</button>
<button id="next">Next Question</button>
<button id="submit">Submit Quiz</button>
<div id="results"></div>复制代码

buildQuiz函数中,增加一个slide类的div元素来放置问题和答案。

output.push(
  `<div class="slide">
    <div class="question"> ${currentQuestion.question} </div>
    <div class="answers"> ${answers.join("")} </div>
  </div>`
);复制代码

然后,开始放入CSS样式,通过z-indexopacity动画属性来显示或隐藏。

.slide{
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.5s;
}
.active-slide{
  opacity: 1;
  z-index: 2;
}
.quiz-container{
  position: relative;
  height: 200px;
  margin-top: 40px;
}复制代码

然后,增加JavaScript逻辑

首先需要变量来指向按钮和现在所在页面。

const previousButton = document.getElementById("previous");
const nextButton = document.getElementById("next");
const slides = document.querySelectorAll(".slide");
let currentSlide = 0;复制代码

这是显示页面的函数:

function showSlide(n) {
  //移出类,达到隐藏功能
  slides[currentSlide].classList.remove('active-slide');
  //增加类,达到显示功能
  slides[n].classList.add('active-slide');
  //更新现在页面页码
  currentSlide = n;
  //如果处于第一页,则没有前一页按钮
  if(currentSlide===0){
    previousButton.style.display = 'none';
  }
  else{
    previousButton.style.display = 'inline-block';
  }
  //如果最后一页,则没有后一页按钮
  if(currentSlide===slides.length-1){
    nextButton.style.display = 'none';
    submitButton.style.display = 'inline-block';
  }
  else{
    nextButton.style.display = 'inline-block';
    submitButton.style.display = 'none';
  }
}
showSlide(0);复制代码

然后,编写函数来实现按钮功能

function showNextSlide() {
  showSlide(currentSlide + 1);
}

function showPreviousSlide() {
  showSlide(currentSlide - 1);
}

previousButton.addEventListener("click", showPreviousSlide);
nextButton.addEventListener("click", showNextSlide);复制代码

效果如下:

增加计时功能

一般测验都需要有时间限制。所以首先先更新HTML结构:

<div id="timer"></div>复制代码

放置在标题下面,题目上方

大致需要以下两个函数,一个是将时间分割便于后来显示,一个是初始化时钟。

//初始化时钟
        function initializeClock(id, endtime) {
          var clock = document.getElementById(id);


          function updateClock() {
            var t = getTimeRemaining(endtime);
            //显示剩余时间     
            clock.innerHTML = "剩余时间:"+('0' + t.hours).slice(-2)+":"+('0' + t.minutes).slice(-2)+':'+('0' + t.seconds).slice(-2)

            //时间到了,自动交卷
            if (t.total <= 0) {
              showResults();
              clearInterval(timeinterval);
            }
          }

          updateClock();
          // 每秒自动更新
          var timeinterval = setInterval(updateClock, 1000);
        }复制代码

然后通过下面调用:

//设置计时1分钟
    var deadline = new Date(Date.parse(new Date())+1*60*1000);
        initializeClock('timer', deadline);复制代码

但是这样设置时间还需要计算成毫秒,所以需要另一个函数,只需要输入计时时长,就直接就能换算成毫秒。

// 设置截止时间
        function setDeadline(hours,minutes,seconds){
          var deadline = hours*60*60*1000+minutes*60*1000+seconds*1000
          return new Date(Date.parse(new Date())+deadline);
        }复制代码

最终效果如下:

参考资料:

How to Make a Simple JavaScript Quiz
Build a Countdown Timer in Just 18 Lines of JavaScript


喜欢此文的同学,可以关注我的知乎专栏前端乱炖大杂烩

关注下面的标签,发现更多相似文章
评论