使用Vue自定义指令实现Select组件


Posted in Javascript onMay 24, 2018

本篇文章教大家写一个非常简单的Select组件,想必很多人都写过Select,毕竟它太常用了,但是本篇文章的示例使用到了Vue的自定义指令,如果你对Vue自定义指令不怎么熟悉的话,本篇文章或许会让您有所收获!

完成的效果图如下:

使用Vue自定义指令实现Select组件 

一、首先,我们简单布局一下:

<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: '地三鲜'
      }
    ],
  }
}

效果是这样:

使用Vue自定义指令实现Select组件 

下面可供选择的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 的布尔值。

效果如下:

使用Vue自定义指令实现Select组件 

第二个,点击下面的选项,将被选择的展示到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值即可,同时,隐藏整个选项内容。

效果如下:

使用Vue自定义指令实现Select组件 

从上面的效果图中可以看到,已经可以正常选择了,但是有一个问题,就是它选项内容展示的时候,我们希望点击其它空白的地方也可以让选择内容隐藏,但是上面的代码并没有解决这个问题,接下来我们来用两种办法来解决它。

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 上面了。效果如下:

使用Vue自定义指令实现Select组件 

到这里基本功能都写完了,可以通过添加 $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 出的东西都是些啥。

使用Vue自定义指令实现Select组件 

从上面的图片可以看出当指令和元素绑定的时候即 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)
    }    
  }
}

代码如上,效果如下:

使用Vue自定义指令实现Select组件 

简单总结一下:这是一个非常简单的小例子,因为需要操作DOM,所以我们选择使用自定义来完成,当然我们也可以使用其它方法。只是,在我们用Vue的时候,如果遇到需要操作DOM的时候,那么可以想想可不可以通过自定义指令来实现呢!

Javascript 相关文章推荐
JS解析XML的实现代码
Nov 12 Javascript
JQuery中的$.getJSON 使用说明
Mar 10 Javascript
js实现鼠标悬停图片上时滚动文字说明的方法
Feb 17 Javascript
跟我学习javascript的最新标准ES6
Nov 20 Javascript
jquery模拟多级复选框效果的简单实例
Jun 08 Javascript
jstree单选功能的实现方法
Jun 07 Javascript
js canvas实现简单的图像扩散效果
Jun 28 Javascript
jQuery滚动条美化插件nicescroll简单用法示例
Apr 18 jQuery
koa大型web项目中使用路由装饰器的方法示例
Apr 02 Javascript
jQuery 查找元素操作实例小结
Oct 02 jQuery
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
Oct 10 jQuery
原生js实现自定义滚动条
Jan 20 Javascript
详解Vue单元测试case写法
May 24 #Javascript
微信小程序通过保存图片分享到朋友圈功能
May 24 #Javascript
karma+webpack搭建vue单元测试环境的方法示例
May 24 #Javascript
react实现点击选中的li高亮的示例代码
May 24 #Javascript
浅谈Webpack 是如何加载模块的
May 24 #Javascript
jquery.onoff实现简单的开关按钮功能(推荐)
May 24 #jQuery
详解javascript中的变量提升和函数提升
May 24 #Javascript
You might like
那些年一起学习的PHP(一)
2012/03/21 PHP
php源代码安装常见错误与解决办法分享
2013/05/28 PHP
PHP读书笔记_运算符详解
2016/07/01 PHP
php+mongodb判断坐标是否在指定多边形区域内的实例
2016/10/28 PHP
TP5框架使用QueryList采集框架爬小说操作示例
2020/03/26 PHP
PHP常用字符串输出方法分析(echo,print,printf及sprintf)
2021/03/09 PHP
兼容IE和Firefox的javascript获取iframe文档内容的函数
2011/08/15 Javascript
jquery 跳到顶部和底部动画2句代码简单实现
2013/07/18 Javascript
基于jquery插件实现常见的幻灯片效果
2013/11/01 Javascript
javascript window.open打开新窗口后无法再次打开该窗口问题的解决方法
2014/04/12 Javascript
jquery mobile的触控点击事件会多次触发问题的解决方法
2014/05/08 Javascript
JavaScript判断FileUpload控件上传文件类型
2015/09/28 Javascript
JS+DIV+CSS排版布局实现美观的选项卡效果
2015/10/10 Javascript
JS显示日历和天气的方法
2016/03/01 Javascript
JavaScript学习笔记之数组求和方法
2016/03/23 Javascript
jQuery Dialog对话框事件用法实例分析
2016/05/10 Javascript
jQuery webuploader分片上传大文件
2016/11/07 Javascript
浅谈js原生拖放
2016/11/21 Javascript
深入理解Webpack 中路径的配置
2017/06/17 Javascript
Seajs源码详解分析
2019/04/02 Javascript
js实现类似iphone的网页滑屏解锁功能示例【附源码下载】
2019/06/10 Javascript
vue使用@scroll监听滚动事件时,@scroll无效问题的解决方法详解
2019/10/15 Javascript
python 利用浏览器 Cookie 模拟登录的用户访问知乎的方法
2019/07/11 Python
keras 特征图可视化实例(中间层)
2020/01/24 Python
Python利用for循环打印星号三角形的案例
2020/04/12 Python
python保留格式汇总各部门excel内容的实现思路
2020/06/01 Python
vscode调试django项目的方法
2020/08/06 Python
Python使用eval函数执行动态标表达式过程详解
2020/10/17 Python
CSS3实现超慢速移动动画效果非常流畅无卡顿
2014/06/15 HTML / CSS
canvas学习笔记之2d画布基础的实现
2019/02/21 HTML / CSS
Canvas实现贝赛尔曲线轨迹动画的示例代码
2019/04/25 HTML / CSS
英国户外装备商店:Ultimate Outdoors
2019/05/07 全球购物
电子商务系毕业生自荐信
2014/05/29 职场文书
2014司机年终工作总结
2014/12/05 职场文书
Nginx设置HTTPS的方法步骤 443证书配置方法
2022/03/21 Servers
Python使用mitmproxy工具监控手机 下载手机小视频
2022/04/18 Python