本篇文章教大家写一个非常简单的Select组件,想必很多人都写过Select,毕竟它太常用了,但是本篇文章的示例使用到了Vue的自定义指令,如果你对Vue自定义指令不怎么熟悉的话,本篇文章或许会让您有所收获!
完成的效果图如下:
一、首先,我们简单布局一下:
<template> <div class="select"> <div class="inner"> <div class="inputWrapper"> <input type="text" readonly placeholder="请选择菜品"> <span class="iconfont icon-zhankaishangxia"></span> </div> <ul class="options"> <li v-for="(item, index) in options" :key="index">{{item.value}}</li> </ul> </div> </div> </template> ...... data() { return { options: [ { value: '西红柿鸡蛋' }, { value: '青椒抱鸡蛋' }, { value: '回锅肉' }, { value: '宫保鸡丁' }, { value: '地三鲜' } ], } }
效果是这样:
下面可供选择的options用的是绝对定位;同时input设置了readonly,使input变的不可输入,整体布局很简单。
二、开始添加功能
接下来,我们要添加两个功能:
- 点击上面的input框,可以切换显示下面的options
- 选择options里的某个选项后让它展示在input里,同时让选项部分消失
这两项目功能都挺简单,先来完成第一个,点击input框切换显示options,借助v-show就好。
<div class="inputWrapper" @click="showOptions = !showOptions"> <input type="text" readonly placeholder="请选择菜品"> <span class="iconfont icon-zhankaishangxia"></span> </div> <ul class="options" v-show="showOptions" v-show="showOptions"> //添加v-show <li v-for="(item, index) in options" :key="index">{{item.value}}</li> </ul> ...... data() { showOptions: false }
如上所示,在选项里添加 v-show="showOptions"
并将 showOptions 初始化为 false 。同时,在包裹 input 的 div 上添加 click 事件来回切换 showOptions 的布尔值。
效果如下:
第二个,点击下面的选项,将被选择的展示到input里,同时让options消失,也不难。
<div class="inputWrapper" @click="showOptions = !showOptions"> <input type="text" readonly placeholder="请选择菜品" :value="selected"> //这里用value绑定一个data值selected <span class="iconfont icon-zhankaishangxia"></span> </div> <ul class="options" v-show="showOptions"> <li v-for="(item, index) in options" :key="index" @click="choose(item.value)">{{item.value}}</li> </ul> ...... data() { return { ...... showOptions: false selected: '' } }, methods: { choose(value) { this.showOptions = false if (value !== this.selected) { this.selected = value } } }
逻辑很简单,在input里用value绑定一个data值,点击选择某个选项后,将选项的内容赋给这个data值即可,同时,隐藏整个选项内容。
效果如下:
从上面的效果图中可以看到,已经可以正常选择了,但是有一个问题,就是它选项内容展示的时候,我们希望点击其它空白的地方也可以让选择内容隐藏,但是上面的代码并没有解决这个问题,接下来我们来用两种办法来解决它。
3、常规的DOM操作 VS Vue自定义指令
其实,实现这个功能并不难,只是要想解决它就需要操作DOM
<div class="inputWrapper" @click.stop="showOptions = !showOptions"> //注意这里的stop修饰器 <input type="text" readonly placeholder="请选择菜品" :value="selected"> <span class="iconfont icon-zhankaishangxia"></span> </div> <ul class="options" v-show="showOptions"> <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li> //还有这里的stop修饰器 </ul> ... data() { return { ...... showOptions: false } } mounted() { let that = this document.addEventListener('click', function() { that.showOptions = false }) }
上面的代码有两点:一个是在mounted后面给整个document添加了点击事件,这样在点击时候就可以将options隐藏,但是,我们在点击输入框部分和选项内容时,我们不希望它触发,而是让它走我们之前写好的逻辑,所以给两个 click 事件都添加了 stop 修饰器来阻止冒泡,这样,点击到它们的时候就不会冒泡到 document 上面了。效果如下:
到这里基本功能都写完了,可以通过添加 $emit 和 props 来进行数据传递,让它更加通用些。但是最后关于点击其它地方让选项部分消失的功能,我们还可以再完善下,可以考虑使用Vue指令的方式实现。
关于Vue指令,官方文档里有比较清楚的说明,如果不是特别明白可以点击这里先看看!
关于Vue自定义指令,在这个例子中需要明白以下基本知识点:
它是用来操作DOM的,所以所有Vue指令都会挂在 template 里的某个元素上
它有4个钩子函数,一是 bind ,它在指令第一次绑定到元素上调用而且只调用一次,这个钩子很重要,我们在这个例子里会用到;第二个是 inserted ,它在元素插入到父元素的时候调用,官方文档里给了一个 v-focus 的例子就用到了它;第三个和第四个分别是 update 和 componentUpdated ,前者是在 vNode 更新时调用,后者是在更新完成后调用;最后是 unbind ,在指令和元素解绑时调用。
这4个钩子函数可以 都至少可以传3个参数 ,第一是 el 就是被绑定指令的元素,第二个 binding , 它是个对象 ,而且 它的一些属性特别有用 ,它的属性包括 name , expression 和 value 等,当然不只这三个,但是我们这个例子要用。举个例子: 假如我写一个自定义指令 v-example="test" ,而这个 test 是我在 methods 里写的一个方法,那么就可以通过 binding.name 拿到 example 字符串,可以通过 binding.value 拿到 test 函数本身并且执行。如果这里不明白没关系接下来我们会说到。
如果仔细观察,它们非常像 Vue 本身的生命周期钩子函数,只是它们是作用在指令上与元素的上的。从 bind 最开始绑定到最后 unbind 解绑完成了一个完整的周期。
好了,我们把之前 mounted 写的DOM操作相关的东西都删掉,开始动手写一个自定义指令。
<ul class="options" v-show="showOptions" v-clickOut="test"> //这里使用了下面的自定义指令,并将一个test方法传递进去了 <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li> </ul> ... methods: { ...... test() { //test函数,它作为参数传递给了指令 console.log('这是一个测试函数') } }, directives: { //这里是自定义指令 clickOut: { // 这里是自定义的v-clickOut指令 bind: function(el, binding) { // bind钩子函数,当它与元素绑定的时候就会执行 console.log('el===>', el) console.log('binding.name===>', binding.name) console.log('binding.expression===>', binding.expression) console.log('binding.value===>', binding.value) } } }
上面的代码都有清楚的注释说明,我们自定义了一个 clickOut 的指令,并且把它挂到了一个元素上,而且给它传了一个 test 方法,我们来看看 console.log 出的东西都是些啥。
从上面的图片可以看出当指令和元素绑定的时候即 bind 的时候,它会执行bind函数获得很多有用的东西,上面我们讲了 bind 函数里有几个重要的参数,从打印出的结果里我们非常清楚地看到,el就是指令绑定的元素本身,binding是一个对象,它获得了很多有用的东西,包括传递进来的函数。
明白了它的基本构造,我们就来继续完善这个指令。
<ul class="options" v-show="showOptions" v-clickOut="test"> <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li> </ul> ... methods: { test() { this.showOptions = false } }, directives: { clickOut: { bind: function(el, binding) { document.addEventListener('click', function(e) { if (el.contains(e.target)) return false if (binding.expression) { binding.value() } }) } }
看下上面改写过的代码做了些啥? 说下逻辑:当我们自定的 v-clickOut 与选项部分的ul元素绑定的时候,我们监听document的click事件,如果点击的元素是被指令绑定的元素的子元素或是被绑定元素本身,那就什么都不做;如果不是,那就执行传递进来的test函数。而test函数执行的结果就是把选项部分隐藏。
逻辑很清楚。
当然我们可以继续完善它。我们给 document.addEventListener
了,也可以在 合适的时候 removeEventListener
,这个合适的时候就是 unbind 钩子函数。
所以我们可以完善下:
...... directives : { clickOut: { bind: function(el, binding) { function handler(e) { if (el.contains(el.target)) return false if (binding.expression) { binding.value() } } el.handler = handler document.addEventListener('click', el.handler) }, unbind: function(el) { document.removeEventListener('click', el.handler) } } }
代码如上,效果如下:
简单总结一下:这是一个非常简单的小例子,因为需要操作DOM,所以我们选择使用自定义来完成,当然我们也可以使用其它方法。只是,在我们用Vue的时候,如果遇到需要操作DOM的时候,那么可以想想可不可以通过自定义指令来实现呢!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]