vue实现自定义多选与单选的答题功能


Posted in Javascript onJuly 05, 2018

本来实现多选单选这个功能,vue组件中在表单方面提供了一个v-model指令,非常的善解“猿”意,

能把我们的多选单选功能很完美的且很强大得双向绑定起来,实现多选单选任意选根本不在话下。

但是,凡事都有一个但是!

但是奈何这个项目设计稿的缘故,使用原生的表单组件是不可能使用了,请看ui图: 

vue实现自定义多选与单选的答题功能 

可悲的是,这个项目两个月后,我才来做项目复盘,

话说也就在此时,我才发现有一种更简单的方式来实现并且应用上v-model,

为什么要为了样式放弃功能然后自己吭哧吭哧傻-滴-呼呼的用js来实现了类似双向绑定的感觉!!!

flag:今天先专注把我费劲巴拉手动搬得砖总结一下,明天(07-05)我再把所谓的最简单的方法做出来贴这里~

这个需求的难点在于以下几点:

1.单选点击后选中状态,需满足如下:X

a) 每次点击只能选中其中一个

b) 当选中时再次点击其他选项需要切换选择对应点击项

c) 选中时点击自身无显示上的反应(同样的逻辑再做一遍也无妨,即再加一遍类名也看不出来)

2.多选样式展示,需满足如下:

a) 同时可以选中多个X

b) 多选已选中状态再次点击取消选中X

3.多选选中项的记录,需满足如下:

a) 选择几个记录几个

b) 选中再取消时需要将本条记录的数据通时消除(依据点击事件,事件点击触发判断哪个被选中了)

4.单选选中项的记录,方便提交数据

5.未点击选项不可提交,并给提示

6.可提交状态,需满足如下:

a) 单选选中任意一个,即可提交。再次修改对提交没有影响

b) 多选至少选中一个可提交,再次修改需判断是不是没选东西

7.第十四题点下一题切换提交按钮

8.快速点击下一题,多次提交

9.点击下一题提交数据后,拿响应结果调取弹层提示用户选择是否正确

=============接下来一 一解决====================

首先先说结构

看似十道题,其实是一道题不停的换数据,所以我的外部结构就是一个form加一个空的div

别问我为什么多余一个空的,我也很无措。

form.question(v-if="state.ExamInfo")
 div

然后题目标题很傻瓜式得使用了h3

h3.qus-title(:data-id="state.ExamInfo.QuestionID") {{state.ExamInfo.ExamQuestionNo}} {{state.ExamInfo.Description}}

选项上,我使用ul>li的形式描述了多个选项

ul.qus-list
  li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" @click="choosed(index)" v-bind:class="{'li-focus' : chooseNum==index}" ref="liId") {{item.Code}}、{{item.Description}}

大致几个属性

  • v-for是为了遍历题中的每一个选项,
  • click绑定了点击当前li时的事件,v-bind同步click绑定了动态的类名,用于展示选中状态。
  • 这里值得注意的一个点也是当时抓虾的一个点是,v-on:click和v-bind:class结合,
  • click的时候,每次把当前点击的li的index值传出去,

然后定义一个变量chooseNum,点击函数中,将参数index赋给他
this.chooseNum = index;

靠这种间接拿到点击索引值得曲线救国方式,在v-bind的监视下,每次点击获得的索引chooseNum

和这几个li中自己的index对上眼以后,就如正确的钥匙对上了合适的锁,类名绑定就成了。

也就是那十几条难题中的第一个被轻松干掉的难题的前半部分: 单选点击后选中状态 。

vue实现自定义多选与单选的答题功能 vue实现自定义多选与单选的答题功能vue实现自定义多选与单选的答题功能

费这么半天劲,才解决一个点啊!我不服!别急,接下来还有好戏。

但其实这个思路还是挺重要的,靠这一点“死皮赖脸”拉关系的劲头,这个法子以后还倒是可以有很多用武之地。

好戏在下一个属性,没错就是ref,这也是我步入万丈深渊一去不复返的梯子啊!

ref

要知道人家可以vue里边的特殊特性,

要知道人家可是很有能力的,

要知道我老是连着打不出妖之道这三个字!我就不行了啊!

好了不皮了。

官网记载ref这个特殊特性,被用来给元素或子组件注册引用信息。 引用信息将会注册在父组件的 $refs 对象上。

如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子组件上,引用就指向组件实例。

我的理解大白话来说,他就是给dom元素或者组件实例一个身份证号,身份证号有的特性他也就有,那就是唯一不重复。

如果配合上v-for,就能获取这一批带有ref特性所组成的数组。

通过数组下标索引出来的个体,也就是对应的dom元素本身或者组件实例本人无疑了。

就好像拿着身份证号去公安局查人一样,快速不说,还很高效有没有,一查一个准!

但需要说明的是,在created钩子中,这个特性拿不到东西,生命周期钩子里只有在mounted里(可能还有后边的钩子里,我没用过不准确)可以用,毕竟你想啊,身份证号虽说一出生就有了,但是只有挂载到网上你才能查到大的嘛!

所以,我究竟用它做了什么呢?那就是多选功能啊!

还是先回到上边说的,绑定了一个事件,并且会传递一个当前点击li的索引号,并且前边也提到过,

ref返回的是数组,有数组有索引号,简直是万事俱备啊。于是乎让我们来呼唤东风(别看了,就是说我们自己)!

在choose点击函数中就有了这么一段:

if(this.$refs.liId[index].className.length <= 0){
  //首先先判断当前li有没有被选中,因为我这里li除了选中状态的有类名,其他没有类名,所以我就这么判断了。
  //这么看有时候舍弃一小丢丢规范的东西反而省力。
  this.$refs.liId[index].className = 'li-focus';// 添加类
}else{
  //当前li已经被选中,那么在多选的逻辑里,是允许人们选中后再取消选中的,所以前端展示层面上把样式去掉。
  this.$refs.liId[index].className = '';// 选中再取消的情况
}

好了,第二个 多选样式 搞定。

vue实现自定义多选与单选的答题功能vue实现自定义多选与单选的答题功能vue实现自定义多选与单选的答题功能

那么接下来,选择的结果呢,能不能来一次“趁火打劫”趁点击的时候偷偷记录下用户的选择?答案当然是可以的啦!

首先说多选功能的趁火打劫吧,就着上边增删类名的热乎劲,紧接着在每次点击时我们记录下当前点击的是谁

// 获取选中结果
    for (let i = 0; i < this.$refs.liId.length; i++) {
      if(this.$refs.liId[i].className.length > 0){
        this.chooseNumStr += this.$refs.liId[i].innerText.substring(0,1);
      }
    }

这一段再次利用了ref的特性,找到当前点击的dom,截取人家选项里的第一个字,那就是ABC or D;

拼接到事先准备好的字符串chooseNumStr中(要发给数据用的),因为这里和后端提前约定的就是将选择结果以字符串的形式提交。

if判断那里,条件再次是利用了li谁有类名就是选了谁的不讲理原则。第三个 多选记录选项功能 问题搞定。

第四个问题是,既然多选记录搞定了,那么单选呢,也应该在每次点击的时候搞定他吧?那是自然!

这里我刚刚突然又想到了一个解决方法,于是这里我将呈现俩个:

1.那就是我当时脑残的解决方法,不过这种方法唯一的好处可能是,

产品大大过来说,那sei,你把选项中的ABCD去掉吧,不好看,那我就傻逼了。

事实上,本来人家设计稿里选项处就没有ABCD,我本着你好我好大家好的原则,说服了他们加上的。。。。。

不废话了,我发现我进入中年了,絮絮叨叨总是进不了正题,或许这和我上课爱走神有关吧。

//索引0-3对应答案A-B,依次类推
    // 注意,这里看看最多的选项是多少个,进行下配置,当前只是配置到了F
    switch(index){
     case 0: this.chooseNumStr = 'A';
     break;
     case 1: this.chooseNumStr = 'B';
     break;
     case 2: this.chooseNumStr = 'C';
     break;
     case 3: this.chooseNumStr = 'D';
     break;
     case 4: this.chooseNumStr = 'E';
     break;
     case 5: this.chooseNumStr = 'F';
     break;
    }

没错,还是在choose中,我判断是单选后,用switch来判断index的值,进而匹配到chooseNumStr的结果。

虽然这种方法很笨拙,而且有超出设置范围的选项的危险,但是,我傻啊!哪有什么方法!

当初就是觉得这么干很不妥,可是直到今天我再看自己的代码才想到更好的解决方案的啊!那他是啥啊?!那就是:

2. 就还是强大的ref登场,规则和选择多选一样,只不过不用for循环。你是不是已经想到了啊哈!

对的,每次单选点的是那个就截取 this.$refs.liId[i].innerText.substring(0,1);   简直soeasy

好了,第四个问题 单选的答案记录 问题解决。

然后,我们接着趁热打铁(才发现他和趁火打劫好像是兄弟啊!),解决下边点击按钮的问题。

需求是没选是灰色,选择选项后可提交:

首先是两个按钮的结构,为了避免后期下一题和提交按钮的交班时我还得判断点击事件是他俩谁和谁,

所以我用了两个按钮,绑了两个事件,把不同功能的事件分开绑定了。

.public-btn(v-if="!isLast" @click="nextItem" v-bind:class="{'public-btn-gray': unclickable}") 下一题
.public-btn(v-else @click="submitItem" v-bind:class="{'public-btn-gray': unclickable}") 提交

可以看到,除了事件我还绑定了class,那个public-btn-gray的生存与否取决于unclickable。

先说没选是灰色的处理:

这个思路上就是肯定是默认提交按钮就是灰色的,也就是有着public-btn-gray类名的。

这里有一个用于描述按钮是不可点击状态的变量unclickable,专门管理按钮是否可点击的。

初始化时是true不可点击的。这样,按钮的gray类名public-btn-gray就加了。

逻辑上,点击按钮的时候先判断这个值,如果为true就提示用户要先选择答案:

if(this.unclickable){
  alert('您还没有选择答案哦!');
}else{// do someting you wanted;}

vue实现自定义多选与单选的答题功能 

然后是 选择选项后可提交 。

那这不好说嘛!我只要点击事件一触发,就把可点击状态放开不就好了嘛!

那好,我是用户,我在如图第15题选择a、c解锁提交按钮,然后我再点击a、c抹掉我的记录,

但这时我的提交按钮已打开,我可以在他毫无防备的情况下趁虚而入(中华文化真博大,这是第三个同意义的成语了)!哈哈哈。

这当然不可以了,直接点击事件就放开下一题按钮,在单选场景下是通的。但是多选的时候我们还要再防御一层。

那就是:

// 置灰提交按钮与否
    if(this.chooseNumStr.length > 0){
    //多选的时候,因为再次点击会把记录抹除,所以chooseNumStr会是动态改变的,
    //如果一个也没选择,多选也好单选也罢,这个字符串肯定是空的,故而判断长度小于0就不让他提交!
     this.unclickable = false;
    }else{
     // 没有选东西,就置灰按钮
     this.unclickable = true;
    }

vue实现自定义多选与单选的答题功能 

耶!第六点 多选功能与下一题 按钮高亮可跳转功能 的结合也完成啦

至此,关于按钮的样式和逻辑就完毕了,每次点击下一题下一题的功能就跑通了。

但是,一直跑到 第十四题点击下一题 ,15题内按钮文案还是下一题,可是这是最后一题了啊,讲点理吧!

好,那就讲理点,让他改成提交,这时下一题和提交按钮换岗。

换岗的时机我是在数据响应回来后判断本题目的题号/id,如果是14题,那么下一题就是最后一题,点击下一题就让提交按钮上岗,下一题退休。

说了这么多,说的最多的是点击下一题。所以在下一题里绑定的事件,就有一个角落是来干这个事的:

// 下一题
if(_this.state.ExamInfo.QuestionID == 14){ 
  //点击下一题,数据响应回来后,新数据替换前,判断如果当前是第14题就改变按钮。
  //判断切换下一题和提交按钮
   _this.isLast = true;
}

然后,提交和下一题俩按钮的样式就靠这个状态值控制,只要在事实的时候改变状态值让他俩交岗即可。

(仔细总结会发现,都是这么一个套路,数据改变某个状态值,状态值绑定在结构上,影响视图的不同展示)
后来,还发现一个隐藏的问题:

点击下一题后,因为是单页应用,页面结构和数据都没有刷新, 上一道题用户选择的结果绑在li上边的样式还需要清空,

所以每次点击下一题甚至提交后都需要在重新填新题目数据时把li的样式选中都清空,也就是把类名都清空。

// 样式清空
for (let i = 0; i < _this.$refs.liId.length; i++) {
   _this.$refs.liId[i].className = '';
}

也需要把上一题的选择数据清空,也就是 chooseNumStr字符串='';

且如果用户翻到下边再还数据,虽然用户看着像换了页面,但其实还在这一页。为了把假象做的更逼真点,需要页面定位到顶部:

// 点击下一题,新页面应该定位到顶头题干位置
document.body.scrollTop = 0;

正当我看着这个天衣无缝的假功能玩的开心的时候,测试大大跑过来说:

~我快速点击多次提交就提交了好多次。。

~exm??!你没事一直点提交干嘛?

~我是测试

~好,大大,你别说了,我这就改嘎。

第⑧个问题: 多次点击下一题/提交按钮

好吧,这个问题确实是我没考虑到,以后做这种表单提交的,肯定要防御用户多次点击提交。

有了上面几次的经验,我现在很会利用data里某个变量来充当状态记录了!

定义一个变量isClicked专门用于看管按钮是否被提交过,如果在可点击的状态下点击过,那么抱歉,逻辑中断!

初始化这个isClicked肯定是没有点击状态,为false,然后在下一题和提交按钮的点击事件中判断

if(!this.isClicked){//没点击过
  //该干啥干啥!
}else{
  //该干嘛干嘛去!
}

所以,到底应该干吗?!

终于说到最后,我好困,如果不是自娱自乐我可能坐着睁眼就睡着了,不,我已经进入梦乡了

说到拿响应结果,,这无非就是 根据相应结果弹层 而已,我不想说什么了。

睡了。晚安世界~

 vue实现自定义多选与单选的答题功能

总结

以上所述是小编给大家介绍的vue实现自定义多选与单选的答题功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript级联下拉列表实例代码(自写)
May 10 Javascript
只需一行代码,轻松实现一个在线编辑器
Nov 12 Javascript
Jquery Ajax方法传值到action的方法
May 11 Javascript
ff chrome和ie下全局动态定位的异同及全局高度的取法
Jun 30 Javascript
jquery特效 点击展示与隐藏全文
Dec 09 Javascript
Javascript es7中比较实用的两个方法示例
Jul 21 Javascript
JS获取一个表单字段中多条数据并转化为json格式
Oct 17 Javascript
微信小程序实现人脸识别
May 25 Javascript
对Vue2 自定义全局指令Vue.directive和指令的生命周期介绍
Aug 30 Javascript
VSCode使用之Vue工程配置eslint
Apr 30 Javascript
vue+elementui实现点击table中的单元格触发事件--弹框
Jul 18 Javascript
JS高级程序设计之class继承重点详解
Jul 07 Javascript
JavaScript实现图片懒加载的方法分析
Jul 05 #Javascript
JavaScript实现浅拷贝与深拷贝的方法分析
Jul 05 #Javascript
手把手教你用Node.js爬虫爬取网站数据的方法
Jul 05 #Javascript
vue使用ElementUI时导航栏默认展开功能的实现
Jul 04 #Javascript
vue两个组件间值的传递或修改方式
Jul 04 #Javascript
jQuery实现炫丽的3d旋转星空效果
Jul 04 #jQuery
jQuery实现table表格checkbox全选的方法分析
Jul 04 #jQuery
You might like
php 设计模式之 单例模式
2008/12/19 PHP
PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据
2012/04/09 PHP
php中serialize序列化与json性能测试的示例分析
2013/04/27 PHP
php判断数组元素中是否存在某个字符串的方法
2014/06/14 PHP
php创建多级目录与级联删除文件的方法示例
2019/09/12 PHP
PHP大文件分割分片上传实现代码
2020/12/09 PHP
JS数组array元素的添加和删除方法代码实例
2015/06/01 Javascript
Javascript原型链的原理详解
2016/01/05 Javascript
移动开发之自适应手机屏幕宽度
2016/11/23 Javascript
使用jsonp实现跨域获取数据实例讲解
2016/12/25 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
angular-tree-component的使用详解
2018/07/30 Javascript
浅析vue 函数配置项watch及函数 $watch 源码分享
2018/11/22 Javascript
JSON字符串操作移除空串更改key/value的介绍
2019/01/05 Javascript
详解如何实现Element树形控件Tree在懒加载模式下的动态更新
2019/04/25 Javascript
详解vue v-model
2020/08/31 Javascript
[01:07:41]IG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[57:29]Alliance vs KG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/17 DOTA
python中while循环语句用法简单实例
2015/05/07 Python
Python中有趣在__call__函数
2015/06/21 Python
使用paramiko远程执行命令、下发文件的实例
2017/10/01 Python
python实现Virginia无密钥解密
2019/03/20 Python
python学习开发mock接口
2019/04/28 Python
简单分析python的类变量、实例变量
2019/08/23 Python
详解python中GPU版本的opencv常用方法介绍
2020/07/24 Python
python3中布局背景颜色代码分析
2020/12/01 Python
澳大利亚窗帘商店:Curtain Wonderland
2019/12/01 全球购物
阿迪达斯新加坡官方网站:adidas新加坡
2019/12/06 全球购物
乌克兰排名第一的在线旅游超市:Farvater.Travel
2020/01/02 全球购物
意大利时尚精品店:Nugnes 1920
2020/02/10 全球购物
电脑教师的教学自我评价
2013/11/26 职场文书
岗位廉洁从政承诺书
2014/03/27 职场文书
经销商年会策划方案
2014/05/29 职场文书
房屋转让协议书
2014/10/18 职场文书
纪念建国70周年演讲稿
2019/07/19 职场文书
Win11 引入 Windows 365 云操作系统,适应疫情期间混合办公模式:启动时直接登录、模
2022/04/06 数码科技