本文通过分段叙述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
})();复制代码
See the Pen
Simple JavaScript Quiz (ES6) V3 (No Params) by SitePoint (
@SitePoint) on
CodePen.
增加页码
想让一页只显示一个问题。所以需要:
- 显示和隐藏问题方式
- 操作测验表的按钮
所以需要升级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-index
和opacity
动画属性来显示或隐藏。
.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);复制代码
效果如下:
See the Pen Simple JavaScript Quiz (ES6) V4 (Stylized) by SitePoint ( @SitePoint) on CodePen.增加计时功能
一般测验都需要有时间限制。所以首先先更新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);
}复制代码
最终效果如下:
See the Pen quize by yuweijie ( @owen1190) on CodePen.参考资料:
How to Make a Simple JavaScript Quiz
Build a Countdown Timer in Just 18 Lines of JavaScript
喜欢此文的同学,可以关注我的知乎专栏前端乱炖大杂烩