答题卡效果
在前端开发中,我们大量使用开源很多UI框架和js框架,让我们使用的越好,做项目越快,但是同时让我们也对最基本的css属性和js最基本和最底层的api都遗忘,所以我们通过自己封装插件和组件,让我们更容易拾起最基本的知识点;以下我会从3个部分总结这个过程:1.原生js和jquery实现基本tab效果,2.使用面向对象js和jquery常用封装方法,3.通过vue封装和优化AnswerSheet组件
核心js封装
/**
* 插件作用:
* 1. 切换效果
* 2. 封装初始化模板,前端不需要构建dom结构
* 3. 对数据的处理,传入数据,修改数据状态,返回更新后数据
* 4. 监测用户正在操作当前数据状态
*/
(function(window) {
function AnswerSheet(options,callback) {
var opts = Object.assign({}, AnswerSheet.defaultOptions, options);
this.callback = callback;
this.opts = opts;
this.insertDom = opts.insertDom;
this.questions = opts.questions;
this.activeFiled = opts.activeFiled;
this.currentIndex = 0;//记录当前题目的索引
this.cardConts = null;//获取所有卡片node
this.btnNodes = null;//下一个按钮node
this.answerLables = null;//当前卡片的答案
this.activedQuestionNode = null;//记录激活状态答案node
this.init();
}
AnswerSheet.prototype = {
constructor: AnswerSheet,
init: function() {
this.initData(this.questions);
this.initTemplate(this.questions);
},
//初始化模板,
initTemplate: function(data) {
let strHtml = ` `;
data.forEach((item, index) => {
strHtml += `
${index + 1}. ${item.question}
${item.answerList.length > 0 && this.renderChild(item.answerList)}
${index > 0 ? '上一题' : ''}
${index + 1}/ ${this.questions.length}
`
})
strHtml += ``
this.insertDom.appendChild(this.strToNode(strHtml));
this.cardConts = document.querySelectorAll(".card_cont");
this.btnNodes = document.querySelectorAll(".prev");
this.answerLables =document.querySelectorAll(".answer-item");
//给答案绑定事件
this.bindEvent(this.answerLables,"click", this.nextHandler);
//给上一个按钮绑定事件
this.bindEvent(this.btnNodes, "click", this.prevHandler);
},
renderChild: function(answerList) {
let strChild = ` `;
answerList.forEach(item => {
strChild += `${item.lable}`
})
strChild += ``
return strChild;
},
//初始化数据
initData: function(data) {
data.forEach(item => {
item.answerList.forEach(child => {
child[this.activeFiled] = false;
})
});
},
//初始化card编号
initCardNum: function() {
},
//点击答案下一个卡片操作
nextHandler(e) {
//设置激活状态
this.setActivedClass(e.target)
//更新数据
this.updateData(e.target.textContent,this.currentIndex);
let restCount = this.cardConts.length - (this.currentIndex + 1);
if(restCount <= 0) {
this.callback && this.callback(this.questions,this.currentIndex);
return;
}
this.cardConts[this.currentIndex].classList.remove("card0");
this.cardConts[this.currentIndex].classList.add("cardn");
restCount >=1 && this.changeCardClass("next",1, this.cardConts[this.currentIndex]);
restCount >=2 && this.changeCardClass("next",2, this.cardConts[this.currentIndex]);
restCount >=3 && this.changeCardClass("next",3, this.cardConts[this.currentIndex]);
this.currentIndex += 1;
},
//点击上一个按钮卡片操作
prevHandler() {
let rest2 = this.currentIndex;
let rest = this.cardConts.length - (this.currentIndex + 1);
if(rest2 <= 0) {
alert("上面没题了")
return
}
//把当前变为cardn
this.cardConts[this.currentIndex].classList.remove("card0");
this.cardConts[this.currentIndex].classList.add("card1");
//把上一个变为card0
rest2 >=1 && this.changeCardClass("prev", -1, this.cardConts[this.currentIndex])
//把下一个变为card0
rest >=1 && this.changeCardClass("prev",1, this.cardConts[this.currentIndex]);
//把下下个变为card1
rest >=2 && this.changeCardClass("prev",2, this.cardConts[this.currentIndex]);
//把下下下个变为card2
// rest >=2 && changeCardClass("prev",3, cardConts[currentIndex]);
this.currentIndex -= 1;
},
//获取当前card的相邻的卡片,处理相邻卡片类名变化
changeCardClass(type, num, currentNode) {
let _temp = null;
switch (num) {
case -1:
_temp = currentNode.previousElementSibling;
break;
case 1:
_temp = currentNode.nextElementSibling;
break;
case 2:
_temp = currentNode.nextElementSibling.nextElementSibling;
break;
case 3:
_temp = currentNode.nextElementSibling.nextElementSibling.nextElementSibling;
break;
default:
break;
}
//区分上一个和下一个操作
if(type == "next") {
_temp.classList.remove("card" + num);
_temp.classList.add("card" + (num - 1));
}else {
if(num < 1) {
_temp.classList.remove("cardn");
_temp.classList.add("card0");
}else {
_temp.classList.remove("card" + num);
_temp.classList.add("card" + (num + 1));
}
}
},
//给选中答案设置激活样式
setActivedClass: function(node) {
//把兄弟node去除actice
let siblingNodes = node.parentNode.childNodes;
siblingNodes.forEach(item => {
item.classList.remove("active");
})
node.classList.add("active");
},
//修改数据状态
updateData: function(value, index) {
console.log(value, index);
let question = this.questions[index];
question.answerList.forEach(item => {
item[this.activeFiled] = false;
})
let answer = question.answerList.find(item => item.lable == value);
answer[this.activeFiled] = true;
console.log(this.questions)
},
//绑定事件
bindEvent: function(node, event, cb) {
if(node.length >= 0) {
Array.prototype.forEach.call(node, item => {
item.addEventListener(event, (e) => {
cb && cb.call(this,e);
}, false)
})
}else {
node.addEventListener(event, (e) => {
cb && cb.call(this,e);
}, false)
}
},
//字符串转换成 DOM对象
strToNode: function(strHtml) {
return new DOMParser().parseFromString(strHtml,'text/html').body.childNodes[0];
}
}
AnswerSheet.defaultOptions = {
insertDom: null,//设置要插入的节点
questions: [],//传入题目数据,结构是有要求的,层级感
activeFiled: "checked",//设置答案的激活状态字段
}
return window.AnswerSheet = AnswerSheet;
})(window)
使用
- data - 模拟数据源
var data = [
{
id: "001",
question: "这是第一个问题",
answerList: [
{
id:"001",
lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
},{
id:"002",
lable: "B",
value: 20
},{
id:"003",
lable: "C",
value: 10
},{
id:"004",
lable: "D",
value: 20
}
]
},{
id: "002",
question: "这是第二个问题",
answerList: [
{
id:"001",
lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
},{
id:"002",
lable: "B",
value: 20
},{
id:"003",
lable: "C",
value: 10
},{
id:"004",
lable: "D",
value: 20
}
]
},{
id: "003",
question: "这是第三个问题",
answerList: [
{
id:"001",
lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
},{
id:"002",
lable: "B",
value: 20
},{
id:"003",
lable: "C",
value: 10
},{
id:"004",
lable: "D",
value: 20
}
]
},{
id: "004",
question: "这是第四个问题",
answerList: [
{
id:"001",
lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
},{
id:"002",
lable: "B",
value: 20
},{
id:"003",
lable: "C",
value: 10
},{
id:"004",
lable: "D",
value: 20
}
]
},{
id: "005",
question: "这是第五个问题",
answerList: [
{
id:"001",
lable: "A",//这个是前端展示的字段,根据自己业务需求进行设置
value: 10// 这个是后台真实所处理的字段,根据自己业务需求进行设置
},{
id:"002",
lable: "B",
value: 20
},{
id:"003",
lable: "C",
value: 10
},{
id:"004",
lable: "D",
value: 20
}
]
}
]
- html
答题卡
效果
下次更新使用vue封装AnswerSheet组件
使用vue封装组的新功能:
- 根据题目种类变颜色
- 可以人工播放题目功能
万水千山总是情,点波关注行不行呦!!!