使用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 相关文章推荐
使用Javascript和DOM Interfaces来处理HTML
Oct 09 Javascript
js textarea自动增高并隐藏滚动条
Dec 16 Javascript
THREE.JS入门教程(6)创建自己的全景图实现步骤
Jan 25 Javascript
JavaScript自动设置IFrame高度的小例子
Jun 08 Javascript
jQuery查询数据返回object和字符串影响原因是什么
Aug 09 Javascript
得到form下的所有的input的js代码
Nov 07 Javascript
js实现点击图片改变页面背景图的方法
Feb 28 Javascript
详解node服务器中打开html文件的两种方法
Sep 18 Javascript
Vue-Access-Control 前端用户权限控制解决方案
Dec 01 Javascript
jQuery发请求传输中文参数乱码问题的解决方案
May 22 jQuery
使用watch在微信小程序中实现全局状态共享
Jun 03 Javascript
layui的表单提交以及验证和修改弹框的实例
Sep 09 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
解密ThinkPHP3.1.2版本之独立分组功能应用
2014/06/19 PHP
PHP中的插件机制原理和实例
2014/07/08 PHP
老生常谈PHP中的数据结构:DS扩展
2017/07/17 PHP
浅谈PHP中的那些魔术常量
2020/12/02 PHP
破除网页鼠标右键被禁用的绝招大全
2006/12/27 Javascript
JQuery 将元素显示在屏幕的中央的代码
2010/02/27 Javascript
JavaScript中数组对象的那些自带方法介绍
2013/03/12 Javascript
自定义jQuery选项卡插件实例
2013/03/27 Javascript
IE6浏览器中window.location.href无效的解决方法
2014/11/20 Javascript
Vue 过渡(动画)transition组件案例详解
2017/01/22 Javascript
js以及jquery实现手风琴效果
2020/04/17 Javascript
js 获取今天以及过去日期
2017/04/11 Javascript
vue组件实现文字居中对齐的方法
2017/08/23 Javascript
three.js中文文档学习之创建场景
2017/11/20 Javascript
微信小程序实现打开内置地图功能【附源码下载】
2017/12/07 Javascript
详解React 的几种条件渲染以及选择
2018/10/23 Javascript
使用rollup打包JS的方法步骤
2018/12/05 Javascript
详解基于vue-cli3快速发布一个fullpage组件
2019/03/08 Javascript
使用原生JS实现滚轮翻页效果的示例代码
2020/05/31 Javascript
python实现计算资源图标crc值的方法
2014/10/05 Python
Python3多进程 multiprocessing 模块实例详解
2018/06/11 Python
在Python中如何传递任意数量的实参的示例代码
2019/03/21 Python
利用Python实现Shp格式向GeoJSON的转换方法
2019/07/09 Python
Pandas0.25来了千万别错过这10大好用的新功能
2019/08/07 Python
自适应线性神经网络Adaline的python实现详解
2019/09/30 Python
Python序列化与反序列化pickle用法实例
2019/11/11 Python
Django-rest-framework中过滤器的定制实例
2020/04/01 Python
解决安装新版PyQt5、PyQT5-tool后打不开并Designer.exe提示no Qt platform plugin的问题
2020/04/24 Python
css3 pointer-events 介绍详解
2017/09/18 HTML / CSS
中粮集团旗下食品网上购物网站:我买网
2016/09/22 全球购物
AutoShack.com加拿大:北美主要的汽车零部件零售商
2019/07/24 全球购物
工艺员岗位职责
2014/02/11 职场文书
班组建设经验交流材料
2014/05/12 职场文书
小学语文教师年度考核个人总结
2015/02/05 职场文书
初中英语教师个人工作总结
2015/02/09 职场文书
如何使用JavaScript策略模式校验表单
2021/04/29 Javascript