实际项目中的一个需求:
点击文本框, 弹出带有复选框的选项, 然后获取选中项的数据, 传给后面的一个功能. 在文本框输入内容, 也会动态的匹配下拉列表, 并且列表带有全选功能.
朴素的效果图:
我选择了用 vue 实现, 算是 vue 的一次练手吧. 不会写的地方也百度了一下.
难点有两个, 一个是全选. 全选不光是点击全选复选框, 选项跟着选中或不选中. 还包括反向的选择, 就是如果把所有选项选中了, 那么 "全选" 也要跟着选中, 而有任何一项未选中, 那么 "全选" 则处于未选中状态. 也就是说这是个互动的过程. 只有做到这点, 才是一个好的用户体验.
我是在循环数据的每一项加了一个表示选中状态的值 lineCheck. 全选和选项的点击分别写. 点击全选中, 把选项的状态置为和其一致就可以.
点击选项时, 利用 every 方法, 只有每一项为真 (也就是选中), 全选才为真, 否则为假 (未选中).
第二个难点就是输入时的匹配问题, 是在 computed 中写了一个 searchLists, 下拉列表的 for 循环也用的这个数据, 全选时遍历的也是这个数据.
其他 tips:
1) 表单的操作离不开 v-model
2) 点击事件加上 stop 修饰符, 阻止冒泡
3) 复选框的事件用的 change
4) 注意用 label, 这样点击文字也有效果, 体验更佳
5) 点击页面空白处, 隐藏下拉列表
总体来说, 用户体验做的还是不错的.
贴出完整代码:(没有好看的样式, 就是最朴素的效果, 毕竟 CSS 对于前端人员来说是最最简单的)
- <!DOCTYPE html>
- <HTML>
- <head>
- <meta charset="utf-8">
- <title>
- 下拉框全选
- </title>
- <link rel="stylesheet" href="css/this.css" />
- </head>
- <body>
- <div class="m-select-wrap" id="v_app">
- <div class="title">
- <input type="text" placeholder="输入 / 勾选" v-model="searchLine" @click.stop="showList"
- @focus="inputFocus">
- <span class="show-list" @click.stop="toggleList">
- ∨
- </span>
- <span class="select-con">
- 选中了:{{selectCon}}
- </span>
- </div>
- <ul v-if="isShow" @click.stop="showList">
- <li>
- <label>
- <input type="checkbox" v-model="checkAllState" @change="checkAll">
- 全选
- </label>
- </li>
- <li v-for="item in searchLists">
- <label :id="item.lineId">
- <input type="checkbox" v-model="item.lineCheck" @change="checkOne(item)">
- {{item.lineName}}
- </label>
- </li>
- </ul>
- </div>
- <script src="js/vue.js">
- </script>
- <script>
- var lines = [{
- lineId: 'line1',
- lineName: '数据 1',
- lineCheck: false
- },
- {
- lineId: 'line2',
- lineName: '数据 2',
- lineCheck: false
- },
- {
- lineId: 'line3',
- lineName: '数据 3',
- lineCheck: false
- },
- {
- lineId: 'line4',
- lineName: '数据 4',
- lineCheck: false
- },
- {
- lineId: 'line5',
- lineName: '数据 5',
- lineCheck: false
- }] new Vue({
- el: '#v_app',
- data: {
- // 数据
- lineList: lines,
- // 选项的选中状态
- checkAllState: false,
- // 选中的数据
- checkedList: [],
- // 文本框的值
- searchLine: '',
- // 下拉列表是否显示
- isShow: false,
- // 选中的内容
- selectCon: ''
- },
- methods: {
- // 全选
- checkAll: function() {
- for (var i = 0; i < this.searchLists.length; i++) {
- this.searchLists[i].lineCheck = this.checkAllState;
- }
- this.getCheckData();
- },
- // 选择单个
- checkOne: function(item) {
- this.searchLists.every(function(item) {
- return item.lineCheck == true;
- }) ? this.checkAllState = true: this.checkAllState = false;
- this.getCheckData();
- },
- // 获取选中的数据
- getCheckData: function() {
- this.checkedList = this.searchLists.filter(function(item) {
- return item.lineCheck == true;
- })
- // 选中的值显示到输入框中
- this.selectCon = '';
- for (var i = 0; i < this.checkedList.length; i++) {
- this.selectCon += this.checkedList[i].lineName + ',';
- }
- },
- // 切换下拉列表
- toggleList: function() {
- this.isShow = !this.isShow;
- },
- // 显示下拉列表
- showList: function() {
- this.isShow = true;
- },
- // 文本框获得焦点时文字被选中
- inputFocus: function(e) {
- e.currentTarget.select();
- }
- },
- computed: {
- // 输入框筛选列表
- searchLists: function() {
- var _search = this.searchLine;
- if (_search) {
- return this.lineList.filter(function(item) {
- return Object.keys(item).some(function(key) {
- return String(item.lineName).toLowerCase().indexOf(_search) > -1
- })
- })
- }
- return this.lineList;
- }
- },
- mounted: function() {
- var _this = this;
- // 点击页面空白处隐藏下拉列表
- document.addEventListener('click',
- function() {
- _this.isShow = false;
- });
- }
- });
- </script>
- </body>
- </HTML>
CSS:
- @charset "utf-8";
- *{
- margin: 0;padding: 0;list-style: none;box-sizing: border-box;font-size: 100%;
- }
- .m-map-screen{
- width: 100%;margin: 50px auto 100px;height: 40px;
- }
- .m-map-screen .screen-box{
- float: left;position: relative; margin-left: 30px;background: #fff;height: 40px;
- }
- .m-map-screen .screen-box select{
- height: 40px;
- }
- .m-map-screen .screen-box h2.title{
- font-weight: normal;padding:0 10px;border:1px solid #ccc;height: 40px;
- }
- .m-map-screen .screen-data{
- position: absolute;display: none;top: 40px;left: 0;
- }
- .map-con{
- width: 100px;height: 100px;border: 1px solid #ccc;clear: both;margin: 20px 0 0 20px;display: none;
- }
- .m-select-wrap{
- width: 300px;margin: 20px auto 0;
- }
- .m-select-wrap .title{
- width: 300px;position: relative;
- }
- .m-select-wrap input[type="text"]{
- width: 300px;height: 40px;padding: 0 5px;
- }
- .m-select-wrap .select-con{
- position: absolute;left: 105%;white-space: nowrap;line-height: 40px;
- }
- .m-select-wrap .show-list{
- position: absolute; width: 30px;height: 40px;line-height: 38px;border: 1px solid #aaa;right: 0;text-align: center;cursor: pointer;
- }
- .m-select-wrap ul{
- border: 1px solid #ccc;padding:0 30px 10px 10px;
- }
- .m-select-wrap li{
- margin-top: 10px;
- }
来源: http://www.bubuko.com/infodetail-3363619.html