vue自定义指令clickoutside扩展--多个元素的并集作为inside

都是个人理解,如果发现错误,恳请大家批评指正,谢谢。还有我说的会比较啰嗦,因为是以自身菜鸡水平的视角来记录学习理解的过程,见谅。

1.前言

产品使用vue+element作为前端框架。在功能开发过程中,难免遇到使用element的组件没办法满足特殊的业务需要,需要对其进行定制,例如要求选择器的弹出框中,增加搜索过滤(跟目前element的输入建议不太一样)。于是想说说之前修改element组件,并定制为业务组件过程中遇到的问题。


ps:因为对某些组件改动很大,所以是直接拷贝了一份源码,然后再进行修改,但是这样会遇到挺多问题,建议对于vue组件如果改动不大,只是简单功能扩展,就直接使用继承的方式修改。

2.clickoutside指令

element中自定义vue的指令之一,clickoutside顾名思义,就是当鼠标点击了指令所绑定元素的外部时,就会触发绑定方法。用途就以el-select为例,当选择器的下拉框展示时,监听鼠标点击事件,如果鼠标位置在整个选择器外部时,进行隐藏下拉框。

2.1使用方式

引入Clickoutside.js


import Clickoutside from 'element-ui/src/utils/clickoutside'

声明指令使用


directives: { Clickoutside },

模板中正式使用


<div v-clickoutside="handleClickOutside">
</div>

2.2实现介绍

简要说明下原理,首先vue自定义指令本身(不了解可以点击链接查看官网介绍)。主要就是利用vue指令的功能,获取所绑定元素的dom对象dom_A以及传递过来的回调方法fun_A,然后监听浏览器的mousedown和mouseup事件(mousedown作为辅助信息,真正触发传递的回调方法的是mouseup事件),当前事件中鼠标位置对应的dom对象dom_B不属于dom_A,则代表鼠标点击了dom_A外部,触发clickoutside回调方法。

2.3扩展介绍

理论上clickoutside只能也只需要绑定一个元素作为inside,但是一些特殊的原因(可能是代码不够好),要求clickoutside可以选定多个元素作为inside,当鼠标点击了这些元素所构成的inside的外部时,再触发事件。
结合下图,A与B两个元素作为一个inside,当鼠标点击在click1位置时,触发clickoutside,当鼠标点击click2或者click3位置时都不触发clickoutside。

1504257-20181106225427540-1496256447.jpg

2.3扩展实现

要实现上述功能,就必须获取到A和B的dom对象,然后在原先鼠标事件的监听的基础上,判断鼠标位置是否都不包含在A和B中,如果是的话再触发clickoutside。
实现方式为,在A和B的父级parent元素上绑定v-clickoutside:yourClassName="handleClickOutside",在A和B元素上添加同一个class样式,样式名称与指令冒号后面内容一致class="yourClassName"。主要在处理指令绑定时,通过binding.arg即可获取到A和B共有的class,存放在dom变量中。在鼠标放开触发事件处理时,通过class获取到他们的dom对象。

2.3.1使用示例

clickoutside原来的使用方式不受影响,只是添加了多个元素并集作为inside的功能。
引入改为自己修改后的clickoutside.js,声明不变,扩展功能在模板中的使用方式


<div v-clickoutside:exactAreaClassName="handleClickOutside">Parent<div class="exactAreaClassName">A</div><div class="exactAreaClassName">B</div>
</div>

2.3.2代码


// 引入Vue用以判断当前运行环境
import Vue from 'vue'
// element封装的一些常用dom操作,这里on可以先当做是addEventListener的封装
import { on } from 'element-ui/src/utils/dom'
// 所有绑定了clickoutside指令的元素的dom对象数组
const nodeList = []
// 用来做存放于dom对象中clickoutside相关参数对象的key
const ctx = '@@clickoutsideContext'let startClick
let seed = 0
// 鼠标按下时,记录此时事件信息
!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e))
// 鼠标松开时候,遍历绑定clickoutside的节点,进行判断是否在节点外部以触发回调
!Vue.prototype.$isServer && on(document, 'mouseup', e => {nodeList.forEach(node => node[ctx].documentHandler(e, startClick))
})// 是否在特殊限定范围内
function ifInExact (exactElms, target1, taget2) {for (let i = 0; i < exactElms.length; i++) {let elm = exactElms[i]if (elm.contains(target1) || elm.contains(taget2) || elm === target1) return true}return false
}// 是否有特殊限定范围
function ifHasExact (el, exactArea) {if (!exactArea) return falsereturn el.getElementsByClassName(exactArea)
}function createDocumentHandler (el, binding, vnode) {return function (mouseup = {}, mousedown = {}) {if (!vnode ||!vnode.context ||!mouseup.target ||!mousedown.target ||(vnode.context.popperElm &&(vnode.context.popperElm.contains(mouseup.target) ||vnode.context.popperElm.contains(mousedown.target)))) returnlet exactElms = ifHasExact(el, el[ctx].exactArea)// 如果是有特殊限定范围的,则进行判断当前点击是否在 限定范围内if (exactElms) {if (ifInExact(exactElms, mouseup.target, mousedown.target)) {return}// 无特殊限定范围,则判断点击是否在默认的指令所在范围内} else if (el.contains(mouseup.target) || el.contains(mousedown.target) || el === mouseup.target) {return}if (binding.expression &&el[ctx].methodName &&vnode.context[el[ctx].methodName]) {vnode.context[el[ctx].methodName]()} else {el[ctx].bindingFn && el[ctx].bindingFn()}}
}export default {bind (el, binding, vnode) {nodeList.push(el)const id = seed++el[ctx] = {id,documentHandler: createDocumentHandler(el, binding, vnode),methodName: binding.expression,bindingFn: binding.value,// 特殊限定范围的class,限定范围为该class的所有元素的并集exactArea: binding.arg}},update (el, binding, vnode) {el[ctx].documentHandler = createDocumentHandler(el, binding, vnode)el[ctx].methodName = binding.expressionel[ctx].bindingFn = binding.value// 附加 真正起作用部分el[ctx].exactArea = binding.arg},unbind (el) {let len = nodeList.lengthfor (let i = 0; i < len; i++) {if (nodeList[i][ctx].id === el[ctx].id) {nodeList.splice(i, 1)break}}delete el[ctx]}
}

3.最后

以上就是关于clickoutside的学习和扩展。

  • 1.引用element的popup注意事项,如el-select-menu即el-select中的select-dropdown.vue。
  • 2.使用cropperjs制作头像裁剪。浏览器读取本地图片并展示,仿微博头像排版,裁剪后上传服务器。
  • 3.vue指令中的参数vnode学习

ps: 个人GayHub后面关于前端的学习内容也会放在这里,如果发现bug尽量及时改上去。

原文地址:https://segmentfault.com/a/1190000014213030

转载于:https://www.cnblogs.com/lalalagq/p/9919464.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365057.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

35个让人惊讶的 CSS3 动画效果演示

本文收集了35个惊人的 CSS3 动画演示,它们将证明 CSS3 Transform 和 Transition 属性的强大能力。CSS 是网页设计非常重要的一部分,随着越来越多的浏览器对 CSS3 支持的不断完善,设计师和开发者们有了更多的选择。如今,用纯 CSS 就…

可选类型API

Java 8引入了Optional类。 简而言之,不是返回null,而是检查null,而是返回Optional实例,该实例可以设置或不设置值。 这样,您就不会因NullPointerException而失败。 我不会讨论Optional类型是否会完全消除NPE&#xff…

计算机社团活动丰富多彩,描写社团丰富多彩的句子

我们学校的社团活动真是丰富多彩用这个句子开头试着写一段话感恩生命,感谢她给予我们丰富的感情。喜怒哀乐,悲思忧惧,洒洒脱脱,原原本本,痛快淋漓,无拘无束;喜而笑,怒而吼&#xff0…

宝元系统u盘使用说明_教你如何使用U盘安装电脑系统

①到老毛桃官网首页下载老毛桃UEFI版u盘启动盘制作工具安装到电脑上;②准备一个容量在4G以上并能够正常使用的u盘。1第一步将u盘插入电脑usb接口,程序会自动扫描,我们只需在下拉列表中选择用于制作的u盘,然后点击“一键制作”按钮…

IP通信基础 4月1日

广域网数据链路控制规程 1.面向字符型传输规程 2.面向比特型传输规程 面向比特型传输规程分析 帧 标志字段F 地址字段A 控制字段C 信息字段TNFO 帧检验序列FCS 标志字段F 1Byte 1Byte 1Byte 可变长 2Byte 1Byte…

输入人民币/美元后自动判断,输出转换后相对应的值

1 2 作者:pasaulis3 版本:v2.04 日期:2018.11.75 功能:输入人民币/美元后自动判断,输出转换后相对应的值6 7 in_str_valueinput(请输入人民币/美元金额(格式示例:100rmb/100usd))9 u…

支持在iPad中播放的HTML5视频网站推荐

YouTube是最早支持HTML5视频的网站,随着iPad等平板电脑的流行,越来越多的视频网站开始支持HTML5视频播放。下面就向大家分享一些国内的HTML5视频网站,是根据网上信息整理而成,欢迎大家反馈和推荐更多的HTML5视频网站。 腾讯 优酷…

自定义Log4j 2.0

我们越来越接近Log4j 2 GA版本,因此,我们应该开始研究它会给我们带来什么。 我个人有很多理由对Log4j 2.0感到兴奋。 社区很棒,Apache许可证给了我很多自由,最后它受到Apache软件基金会的保护。 嘿,这是一个很棒的软件…

计算机斐波那流程图,循环结构——斐波那契数列.DOC

循环结构——斐波那契数列循环结构——斐波那契数列1.教学目标根据新课标的要求和学生的认知特点,确定本节课的教学目标。(1)知识与技能学生能理解循环结构概念;把握循环结构的三要素:循环的初始状态、循环体、循环的终止条件&…

为什么用链路聚合_H3C ComwareV7平台网络设备可靠性配置——链路聚合

H3C ComwareV7平台网络设备可靠性配置——链路聚合链路聚合简介:链路聚合是通过多个物理接口(通常指以太网接口,串口只在特定场景且与以太网聚合技术等同故不再赘述)捆绑实现的逻辑接口,而这些被捆绑在一起的以太网接口就称为该聚合组的成员端…

用php实现一个双向队列

队列是一种线性表,按照先进先出的原则进行 单向队列:只能从头进,从尾出 双向队列:头尾都可以进出 class DuiLie { private $array array();//声明空数组 public function setFirst($item){ //array_unshift() 函数用于向数组插入…

idea+maven+ssm搭建boot_crm项目遇到的问题

使用ideamavenssm搭建一个boot_crm项目,遇到的问题如下: 1.环境搭建好了,相关配置文件都配置好了,也部署到了tomcat,但是无法启动首页。 通过百度,google找到了,原因是web.xml文件出了问题&…

大型计算机变形,计算机变形病毒的主要特征和发展趋势

计算机变形病毒的主要特征和发展趋势计算机病毒具有一些基本的特性,这些基本特性主要指的是病毒的传染性、破坏性、恶作剧等,这是普通病毒所具备的基本特性,而能用变化自身代码和形状来对抗反病毒手段的变形病毒才是下一代病毒首要的基本特征…

Redis与Jedis排序

在本文中,我们将讨论Redis SORT命令。 Redis提供了SORT命令,我们可以使用该命令从LIST,SET或ZSET中检索或存储排序的值。 我们可以使用最简单的形式在KEY上使用命令,如下例所示: SORT numbers_list这将对键中包含的…

获取 HTML5 网页设计灵感的10个网站推荐

这篇文章向大家推荐10个收集 HTML5 网页作品的网站,让大家感受一下 HTML5 的魅力。作为下一代网页标准,HTML5 增加了很多新标签以及新特性,正引领网页技术革命。希望这些优秀的 HTML5 网页案例能带给大家制作 HTML5 网页的灵感。 HTML5 Gall…

postgresql存图片字段类型_PostgreSQL让人着迷的多态性,另辟蹊径省时又省力

PostgreSQL 让人着迷的地方,不在于他比某些数据库的流行,也不在于比某些数据库的高“贵”, 更不如某些数据库的“简单”。Postgresql 让人无法自拔的是他的”多端变化”, 用开发的角度来说,叫多态性。PG本身支持着太多…

计算机怎么查看U盘品牌,如何查看电脑u盘使用

如何查看电脑u盘使用其实笔记本设置U盘启动也并不困难,小编这就教你怎样设置U盘启动!全是硬货!第一步:我们先把U盘插入笔记本的USB接口上,(注意:请不要通过USB延长线来连接笔记本)插上U盘后重启笔记本本。电…

Maven补全之生命周期(Lifecycle)

Maven补全之生命周期(Lifecycle) Maven生命周期基础概念 Maven是基于生命周期构建的,一个Maven项目的构建是已经被清晰定义的过程。 对于我们使用Maven构建项目来说,POM.xml文件可以确保我们得到自己想要的项目(项目名&#xff0c…

嵌入式-第一季-第2课

第二课 嵌入式系统概述 一.核心概念 嵌入式系统是(1)以应用为中心,以计算机技术为基础,(2)软硬件可剪裁,对(3)功能、可靠性、成本、体积、功耗严格要求的专用…

善用封盖

不久前,在博客文章中 ,我解释了Groovy中的Closure。 这篇博客文章将解释一个使用它们的好例子。 最近,我发现自己不得不为服务AJAX请求的大量后端Controller API编写相同的异常处理逻辑。 就像这样: class ApiRugbyPlayerControl…