美文网首页
答题卡AnswerSheet效果封装(二)原生js封装插件,可以

答题卡AnswerSheet效果封装(二)原生js封装插件,可以

作者: Raral | 来源:发表于2021-04-23 16:29 被阅读0次

    答题卡效果

    在前端开发中,我们大量使用开源很多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 = ` <div id="answer" class="card_wrap">`;
               data.forEach((item, index) => {
                   strHtml += `
                       <div class="card_cont card${index}" style="z-index:${data.length - index}">
                           <div class="card">
                               <p class="card_top">${index + 1}. &nbsp;${item.question}</p>
                               <div class="card_bottom">
                                  ${item.answerList.length > 0 && this.renderChild(item.answerList)}
                                   <div class="answer-bottom">
                                       ${index > 0 ? '<span class="prev" >上一题</span>' : ''}
                                       <span class="answer-currentNum">${index + 1}</span>/ <span class="answer-totalNum">${this.questions.length}</span>
                                   </div>
                               </div>
                           </div>
                       </div>
                   `
               })
               strHtml += `</div>`
               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 = ` <div class="answer-wrapper">`;
               answerList.forEach(item => {
                   strChild += `<div class="answer-item" >${item.lable}</div>`
               })
               strChild += `</div>`
               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
    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <meta http-equiv="X-UA-Compatible" content="IE=edge">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <title>答题卡</title>
       <style>
           * {
               margin: 0;
               padding: 0;
           }
           .wrapper {
               padding-top: 34px;
               padding-left: 12px;
               padding-right: 12px;
               padding-bottom: 30px;
               background-color: #999;
           }
           .card_wrap {
               width: 100%;
               height: 500px;
               position: relative;
               overflow: hidden;
           }
           .card_cont {
               width: 100%;
               height: 452px;
               box-sizing: border-box;
               margin: 0 auto;
               position: absolute;
               border-radius: 10px;
               /* background-color:rgb(59, 178, 115); */
               background-color: #fff;
               box-shadow: 1px 1px 2px #ddd;
               position: absolute;
               bottom: 0;
               display: block;
               transition: all 1s;
           }
           /* 表示在视图第一个卡片状态 */
           .card0 {
               transform: scale(1,1) translate(0, 0) !important;
           }
             /* 表示在视图第二个卡片状态 */
           .card1 {
               transform: scale(.85,.85) translate(0, -62px) !important;
           }
             /* 表示在视图第三个卡片状态 */
           .card2 {
               transform: scale(.72,.72) translate(0, -135px) !important;
           }
          
           .next {
               color: #f00;
               position: relative;
               right: 0;
               bottom:0;
           }
           .cardn {
               transform: translate(0, -1000px);
           }
    
    
           /* 卡片样式 */
           .card {
               /* height: 3.75rem; */
               /* line-height: 3.75rem; */
           }
           .card .card_top {
               line-height: 60px;
               text-indent: 40px;
               color: #fff;
                background-color:rgb(59, 178, 115);
           }
           .card .card_bottom {
               background-color: #fff;
    
    
           }
           .card .card_bottom .answer-wrapper {
    
           }
           .answer-item {
               color: rgb(59, 178, 115);
               font-size: 14px;
               line-height: 30px;
               text-align: center;
               border-radius: 20px;
               border: 1px solid rgb(59, 178, 115);
               width: 60%;
               margin: 20px auto;
           }
           .answer-bottom {
               color: rgb(59, 178, 115);
               text-align: right;
               margin-top: 20px;
               width: 60%;
               margin: 60px auto;
           }
           .prev {
               float: left;
           }
           .active {
               background-color: rgb(59, 178, 115);
               color: #fff;
           }
           
    
       </style>
    </head>
    <body>
       <div class="wrapper"></div>
       
    </body>
    <script src="./js/data.js"></script>
    <script src="./js/answerSheet.js"></script>
    <script>
     new AnswerSheet({
       insertDom: document.getElementsByClassName("wrapper")[0],
       questions: data
     }, function(data,index) {
       alert("做完题了,可以进行自己的操作逻辑")
       alert("提交数据"+ JSON.stringify(data));
     })
    </script>
    </html>
    

    效果

    1619167053(1).jpg

    下次更新使用vue封装AnswerSheet组件
    使用vue封装组的新功能:

    1. 根据题目种类变颜色
    2. 可以人工播放题目功能
      万水千山总是情,点波关注行不行呦!!!

    相关文章

      网友评论

          本文标题:答题卡AnswerSheet效果封装(二)原生js封装插件,可以

          本文链接:https://www.haomeiwen.com/subject/cfacrltx.html