vue2使用use注册自定义指令实现输入控制与快捷复制

使用场景

在一些form表单填写内容的时候,要限制输入的内容必须是数值、浮点型,本来el-input-number就可以实现,但是它本身带那个数值控制操作,等一系列感觉不舒服的地方。如果只是使用el-input该多好,只要监听一下输入的内容就好了。于是就可以考虑使用注册指令的方式,限制输入的内容。

因为只是限制数值和浮点型,数值挺好的,限制浮点类型最好是可以传入参数,限制具体多少位数。

版本环境

vue的版本是^2.6.12,elementui的版本是^2.15.6,使用到Vue.directive()方法,浏览器的requestAnimationFrame方法、cancelAnimationFrame方法,文档对象document的execCommand方法。

文件位置

与index.js同级的文件夹中。src/directive/input.js

引入方式

在入口文件内引用,

import App from './App'import "@/directive/input.js"// 省略其他new Vue({el: '#app',router,store,render: h => h(App)
})

复制功能实现

Vue.directive("copy", {bind(el) {el.$value = el.innerTextel.handler = () => {const textarea = document.createElement("textarea")textarea.readOnly = "readonly"textarea.style.position = "absolute"textarea.style.left = "-9999px"textarea.value = el.innerText.trim()document.body.appendChild(textarea)textarea.select()const result = document.execCommand("Copy")if (result) {Message.success("复制成功!")}document.body.removeChild(textarea)}el.addEventListener("click", el.handler)},unbind(el) {el.removeEventListener("click", el.handler)}
})

注册指令过程中有几个钩子函数

实现原理就是为指令绑定的组件设置一个点击事件,在销毁的时候注销掉那个点击事件。

在dom文档流中添加一个textarea文本域,然后设置一系列的样式使其不会出现在视图窗口中,为它赋值,将其添加到文档流中,然后选中整个文本域的内容。然后调用文档流的document.execCommand("Copy")方法,将文本内容复制出来。

这个复制内容功能做的比较简单,因为复制出来的内容是取的元素的innerText的,所以最好是在那种span标签使用这个指令。

使用方式

<div class="page-head-text"><span>订单编号:</span><el-tooltip content="复制单号" type="light"><span v-copy class="pointer" style="color: #1890ff">{{ form.orderSn }}</span></el-tooltip>
</div>

限制整数,浮点数的输入

因为是限制输入类型,因此对于绑定的元素需要进行判断,判断是否是一个Input类型。如果不是的话,就直接通过querySelector去寻找input标签。

需要给指令传递参数,用来区分是想进行整数限制,还是浮点数限制。需要注意的是,这个参数和赋值是两个概念。

v-filter:int  其中的int是一个参数,让我们知道这个输入框限制的是整数。

v-filter:decimals  其中的decimals也是一个参数,让我们知道这个输入框限制的是浮点数。如果我们想设置这个浮点数最多两位呢?在组件里面要怎么用?

这就是参数和赋值的区别。以限制两位浮点数为例

v-filter:decimals="2"

在钩子函数的四个参数之一binding对象中,value对应的是2,arg对应的是decimals

Vue.directive("filter", {bind(el, binding) {if (el.tagName.toLowerCase() !== "input") {el = el instanceof HTMLInputElement ? el : el.querySelector("input")}switch (binding.arg) {case "int":intFilter(el)breakcase "decimals":let decimalsFilterO = new DecimalsBinding()decimalsFilterO.decimalsFilter(el, binding)break}},unbind(el, binding) {// 解除事件监听switch (binding.arg) {case "int":el.removeEventListener("input", intFilterEx, true)breakcase "decimals":el.removeEventListener("input", decimalsFilterEx, true)breakdefault:break}}
})

整数限制

其中intFilter实现

const intFilter = function (el) {el.addEventListener("input", intFilterEx, true)
}
const intFilterEx = e => {e.target.removeEventListener("input", intFilterEx, true)let stop = requestAnimationFrame(() => {let num = e.target.value.replace(/\D/g, "").replace(".", "")e.target.value = ""e.target.value = num.replace(/^0([0-9])/g, "$1") || 0e.target.dispatchEvent(new Event("input"))e.target.addEventListener("input", intFilterEx, true)window.cancelAnimationFrame(stop)}, 1)
}

requestAnimationFrame是浏览器的一个事件,在mdn中有详细的解释

https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

 浮点数限制

function decimalsFilterEx(e) {e.target.removeEventListener("input", this.decimalsFilterEx, true)let re = new RegExp(`^\\D*([0-9]\\d*\\.?\\d{0,${this.decimalsBinding.value}})?.*$`)let stop = requestAnimationFrame(() => {e.target.value = e.target.value.replace(re, "$1").replace(/^0([0-9])/g, "$1")e.target.dispatchEvent(new Event("input"))e.target.addEventListener("input", this.decimalsFilterEx, true)window.cancelAnimationFrame(stop)}, 1)
}
class DecimalsBinding {decimalsBinding = {}decimalsFilter = function (el, binding) {this.decimalsBinding = bindingel.addEventListener("input", this.decimalsFilterEx, true)}decimalsFilterEx = decimalsFilterEx.bind(this)
}

实现方式 

 第一步,创建一个对象,DecimalsBinding对象有三个属性。decimalsBinding用来存储钩子函数中的binding值信息;decimalsFilter用来设置对象存储的binging值,以及为el添加input事件监听;decimalsFilterEx是事件的执行内容。

这里有一些绕,因为在整个input.js文件中,定义了一个decimalsFilterEx函数方法。而且DecimalsBinding里面也有一个同名的属性。

在new DecimalsBinding()创建实例的时候,对象的decimalsFilterEx与input.js文件中的decimalsFilterEx是同一个,因为bind方法改变了外围的decimalsFilterEx的this指向。此时decimalsFilterEx里面的this指向的是实体类

第二步  decimalsFilter为输入框绑定input事件,在这个事件中,先移除原先绑定的监听事件,设定正则校验,拿到输入框的值,使用replace替换里面的值。

然后重新绑定input事件

requestAnimationFrame是一个异步方法,像setTimeOut一样,会返回一个ID值,用来清除这个执行。

requestAnimationFrame的刷新频率和浏览器的渲染频率一样。因此几乎是无感限制。其他监听方法会有几帧显示输入的内容,然后立马消失。这种方法会让用户体验更好一点。

最后就是cancelAnimationFrame方法,清除这个渲染。因为是嵌套设置,如果不清除的话,就像是在setInterval里面再次调用setInterval一样。

 完整文件代码

import { Message } from "element-ui"
import Vue from "vue"const intFilter = function (el) {el.addEventListener("input", intFilterEx, true)
}
const intFilterEx = e => {e.target.removeEventListener("input", intFilterEx, true)let stop = requestAnimationFrame(() => {let num = e.target.value.replace(/\D/g, "").replace(".", "")e.target.value = ""e.target.value = num.replace(/^0([0-9])/g, "$1") || 0e.target.dispatchEvent(new Event("input"))e.target.addEventListener("input", intFilterEx, true)window.cancelAnimationFrame(stop)}, 1)
}function decimalsFilterEx(e) {e.target.removeEventListener("input", this.decimalsFilterEx, true)let re = new RegExp(`^\\D*([0-9]\\d*\\.?\\d{0,${this.decimalsBinding.value}})?.*$`)let stop = requestAnimationFrame(() => {e.target.value = e.target.value.replace(re, "$1").replace(/^0([0-9])/g, "$1")e.target.dispatchEvent(new Event("input"))e.target.addEventListener("input", this.decimalsFilterEx, true)window.cancelAnimationFrame(stop)}, 1)
}
class DecimalsBinding {decimalsBinding = {}decimalsFilter = function (el, binding) {this.decimalsBinding = bindingel.addEventListener("input", this.decimalsFilterEx, true)}decimalsFilterEx = decimalsFilterEx.bind(this)
}Vue.directive("copy", {bind(el) {el.$value = el.innerTextel.handler = () => {const textarea = document.createElement("textarea")textarea.readOnly = "readonly"textarea.style.position = "absolute"textarea.style.left = "-9999px"textarea.value = el.innerText.trim()document.body.appendChild(textarea)textarea.select()const result = document.execCommand("Copy")if (result) {Message.success("复制成功!")}document.body.removeChild(textarea)}el.addEventListener("click", el.handler)},unbind(el) {el.removeEventListener("click", el.handler)}
})Vue.directive("filter", {bind(el, binding) {if (el.tagName.toLowerCase() !== "input") {el = el instanceof HTMLInputElement ? el : el.querySelector("input")}switch (binding.arg) {case "int":intFilter(el)breakcase "decimals":let decimalsFilterO = new DecimalsBinding()decimalsFilterO.decimalsFilter(el, binding)break}},unbind(el, binding) {// 解除事件监听switch (binding.arg) {case "int":el.removeEventListener("input", intFilterEx, true)breakcase "decimals":el.removeEventListener("input", decimalsFilterEx, true)breakdefault:break}}
})export default Vue

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

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

相关文章

pycharm无法添加python解释器的解决方法

出现该错误的原因是先前创建过重名的解释器&#xff08;虚拟环境&#xff09;&#xff0c;在pycharm配置中没有完全删除干净。解决方法如下&#xff1a; 首先在文件->设置界面&#xff0c;找到解释器设置。 然后先按图所示点击全部显示虚拟环境&#xff1a; 接着将无法添…

如何快速掌握一门编程语言

学习一门新的编程语言可能是一个具有挑战性的过程&#xff0c;但通过一些系统的方法&#xff0c;可以大大加快这个过程。 目录 第一步&#xff1a;通过书籍和视频课程掌握基本语法1. **学习编程语言的基础知识**2. **掌握字符串处理**3. **掌握正则表达式和解析器**4. **掌握面…

大数据开发如何快速进阶

目录 1. 个人经验与心得分享1.1 试错的价值与机会把握1.2 投入产出比的考量1.3 刻意练习与技能提升1.4 目标设定与职业规划1.5 自我驱动与成长1.6 第一性原理的应用 2. 大数据开发领域的挑战与机遇2.1 技术革新的挑战2.2 数据治理的难题2.3 人才短缺的问题2.4 投入产出比的考量…

权限类漏洞解析——功能权限篇

上一篇【一文理解权限类漏洞产生的原因之未授权篇】有讲过未授权漏洞产生的原因&#xff0c;但是在我实际的挖洞过程中&#xff0c;其实遇见很少&#xff0c;我有印象的好像只有几个非核心站点的中危。 但是对于另一类权限漏洞&#xff0c;功能及数据权限相关的漏洞就不一样了…

计算机图形学入门23:蒙特卡洛路径追踪

1.前言 前面几篇文章介绍了Whitted-style光线追踪&#xff0c;还介绍了基于物理渲染的基础知识&#xff0c;包括辐射度量学、BRDF以及渲染方程&#xff0c;但并没有给出解渲染方程的方法&#xff0c;或者说如何通过该渲染方程计算出屏幕上每一个坐标的像素值。 Whitted-style光…

SQLServer:从数据类型 varchar 转换为 numeric 时出错。

1.工作要求 计算某两个经纬度距离 2.遇到问题 从数据类型 varchar 转换为 numeric 时出错。 3.解决问题 项目版本较老&#xff0c;使用SQLServer 2012 计算距离需执行视图&#xff0c;如下&#xff1a; SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO ALTER view vi_ord…

【坚果识别】果实识别+图像识别系统+Python+计算机课设+人工智能课设+卷积算法

一、介绍 坚果识别系统&#xff0c;使用Python语言进行开发&#xff0c;通过TensorFlow搭建卷积神经网络算法模型&#xff0c;对10种坚果果实&#xff08;‘杏仁’, ‘巴西坚果’, ‘腰果’, ‘椰子’, ‘榛子’, ‘夏威夷果’, ‘山核桃’, ‘松子’, ‘开心果’, ‘核桃’&a…

CTO透露GPT-5内幕,OpenAI 以36亿美元收购数据库初创公司

目录 01 GPT-5 02 OpenAI收购Rockset 2.1 谁是Rockset&#xff1f; 2.2 OpenAI的目的是什么&#xff1f; 01 GPT-5 虽然GPT-4的视频通话功能尚未全面推广&#xff0c;但OpenAI的CTO已经对即将到来的GPT-5给出了新的暗示。 不久前&#xff0c;Mira回到母校达特茅斯工程学…

springboot 自定义的全局捕获异常失效

背景&#xff1a;springbootspringcloud 分布式微服务。 问题&#xff1a;公共模块在使用RestControllerAdvice全局捕获异常时&#xff0c;捕获不到子服务抛出的相应异常 首先看一下全局异常组件有么有被扫描到 如何查看&#xff0c;很简单只需要写一段类加载打印代码&#x…

剪映数字人口播原理终于搞清楚了

剪映版本升级了,新版本支持数字人定制,于是我赶紧申请了使用资格 目前的价格是49元单个价格/30天 支付49元之后剪映要求上传2.5至10分钟的视频 接着要阅读一段话并录制视频上传 第三步提交,提交完成之后大概两三个小时就会有一个特定数字人形象出现:

嵌入式c语言3——自定义数据类型

结构体struct&#xff0c;共用体union 结构体中定义变量&#xff0c;首尾地址相连 对于union&#xff0c;其包含变量对起始地址相同 由于其起始地址相同&#xff0c;则改变其中某一变量值时有可能使得另一个变量值发生改变 enum 枚举&#xff0c;可以用来定义一堆整形常量构成…

深入详解RocketMQ源码安装与调试

1.源码下载 http://rocketmq.apache.org/dowloading/releases/ 2. 环境要求 64位系统JDK1.8(64位)Maven 3.2.x

性价比蓝牙耳机怎么选?百元高性价比蓝牙耳机推荐

在现代社会中&#xff0c;蓝牙耳机已经成为人们日常生活中必不可少的配件之一。对于许多消费者来说&#xff0c;找到一款高性价比且价格在百元左右的蓝牙耳机是非常重要的。市面上有许多价格不菲的蓝牙耳机&#xff0c;性价比蓝牙耳机怎么选&#xff1f;如何在有限预算下找到性…

ODN网络弱光聚类定界与整治

01 ODN网络弱光运维现状 ODN网络是家庭宽带连接系统-无源光网络 (PON) 的重要组成部分&#xff0c;是连接局端 OLT 和用户 ONT 之间的光路通道&#xff0c;其质量直接影响整个PON系统的性能及可靠性。ODN光纤链路包括OLT PON口、ODF、主干光纤、一级分光器、分支光纤、二级分光…

Unity Shader技巧:实现带投影机效果,有效避免边缘拉伸问题

这个是原始的projector 投影组件,边缘会有拉伸 经过修改shader 后边缘就没有拉伸了 (实现代码在文章最后) 这个着色器通过检查每个像素的UV坐标是否在定义的边界内,来确定是否应用黑色边框。如果UV坐标处于边缘区域,那么像素颜色会被强制设为黑色,从而在投影图像周围形成一…

240703_昇思学习打卡-Day15-K近邻算法实现红酒聚类

KNN(K近邻)算法实现红酒聚类 K近邻算法&#xff0c;是有监督学习中的分类算法&#xff0c;可以用于分类和回归&#xff0c;本篇主要讲解其在分类上的用途。 文章目录 KNN(K近邻)算法实现红酒聚类算法原理数据下载数据读取与处理模型构建--计算距离模型预测 算法原理 KNN算法虽…

日产X-Trail | 压电式喷油器故障

故障现象 冷启动正常&#xff0c;但超车或者发动机处于负荷状态时就会出现熄火。熄火后无法重新发动&#xff0c;要等发动机完全冷却下来才能再次启动。 有两个故障代码&#xff1a;“P2146&#xff1a;喷油器1和2电源对地短路” 和 “P2146&#xff1a;喷油器3和4电源对地短…

vscode python调试,找不到控制调试工具栏,被隐藏了

问题&#xff1a; 如图所示&#xff0c;最开始蓝框中的调试台被莫名其妙的隐藏了&#xff0c;没法进行调试。 解决办法&#xff1a; 打开设置输入调试点击调试&#xff08;31&#xff09;找到红框选的那个选项&#xff0c;选择floating

Django QuerySet对象,all()方法

all()方法 在Django中&#xff0c;all()方法是QuerySet对象的一个方法&#xff0c;用于获取模型的所有实例。 当你调用ModelName.objects.all()时&#xff0c;Django会生成一个SQL查询&#xff0c;从数据库中获取该模型的所有记录&#xff0c;并返回一个QuerySet对象&#xf…

匠心独运:红酒与手工艺的很好结合

在岁月的长河中&#xff0c;红酒与手工艺都以其不同的魅力和技艺&#xff0c;书写着各自的故事。当这两者相遇&#xff0c;仿佛是一场跨越时空的对话&#xff0c;不仅展现了匠心独运的技艺之美&#xff0c;更在无声中诉说着对品质与生活的热爱。今天&#xff0c;就让我们一起探…