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.innerText
    el.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)
        break
      case "decimals":
        let decimalsFilterO = new DecimalsBinding()
        decimalsFilterO.decimalsFilter(el, binding)
        break
    }
  },
  unbind(el, binding) {
    // 解除事件监听
    switch (binding.arg) {
      case "int":
        el.removeEventListener("input", intFilterEx, true)
        break
      case "decimals":
        el.removeEventListener("input", decimalsFilterEx, true)
        break
      default:
        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") || 0
    e.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 = binding
    el.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") || 0
    e.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 = binding
    el.addEventListener("input", this.decimalsFilterEx, true)
  }
  decimalsFilterEx = decimalsFilterEx.bind(this)
}

Vue.directive("copy", {
  bind(el) {
    el.$value = el.innerText
    el.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)
        break
      case "decimals":
        let decimalsFilterO = new DecimalsBinding()
        decimalsFilterO.decimalsFilter(el, binding)
        break
    }
  },
  unbind(el, binding) {
    // 解除事件监听
    switch (binding.arg) {
      case "int":
        el.removeEventListener("input", intFilterEx, true)
        break
      case "decimals":
        el.removeEventListener("input", decimalsFilterEx, true)
        break
      default:
        break
    }
  }
})

export default Vue

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

爬虫笔记20——票星球抢票脚本的实现

以下内容仅供交流学习使用&#xff01;&#xff01;&#xff01; 思路分析 前面的爬虫笔记一步一步走过来我们的技术水平也有了较大的提升了&#xff0c;现在我们来进行一下票星球抢票实战项目&#xff0c;实现票星球的自动抢票。 我们打开票星球的移动端页面&#xff0c;分…

身份证OCR识别的深度解读

引言 随着信息技术的飞速发展&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术在各个领域得到了广泛应用。身份证OCR识别&#xff0c;作为OCR技术的一个重要分支&#xff0c;以其高效、准确的特点&#xff0c;在身份验证、信息录入等方面发挥着重要作用。本文将深入解…

【Linux】Linux用户,用户组,其他人

1.文件拥有者 初次接触Linux的朋友大概会觉得很怪异&#xff0c;怎么“Linux有这么多用户&#xff0c;还分什么用户组&#xff0c;有什用呢&#xff1f;”&#xff0c;这个“用户与用户组”的功能可是相当健全而且好用的一个安全防护措施。 怎么说呢&#xff1f;由于Linux是个…

Chapter10 高级纹理——Shader入门精要学习笔记

Chapter10 高级纹理 一、立方体纹理1.基本概念①组成②采样 2.天空盒子 Sky Box3.环境映射三种方法①特殊布局的纹理创建②手动创建Cubemap——老方法③脚本生成 4.反射5.折射6.菲涅尔反射 二、渲染1.镜子效果2.玻璃效果3.渲染纹理 vs GrabPass 三、程序纹理1.简单程序纹理2.Un…

使用 bend-ingest-kafka 将数据流实时导入到 Databend

作者&#xff1a;韩山杰 Databend Cloud 研发工程师 https://github.com/hantmac Databend是一个开源、高性能、低成本易于扩展的新一代云数据仓库。bend-ingest-kafka 是一个专为 Databend 设计的实时数据导入工具&#xff0c;它允许用户从 Apache Kafka 直接将数据流导入到 D…

MacOS下更新curl

苹果自带的curl不支持Https,我们可以通过curl -V看到如下结果 curl 7.72.0 (x86_64-apple-darwin18.6.0) libcurl/7.72.0 zlib/1.2.12 libidn2/2.3.7 librtmp/2.3 Release-Date: 2020-08-19 Protocols: dict file ftp gopher http imap ldap ldaps pop3 rtmp rtsp smtp telne…

LabVIEW汽车ECU测试系统

开发了一个基于LabVIEW开发的汽车发动机控制单元&#xff08;ECU&#xff09;测试系统。该系统使用了NI的硬件和LabVIEW软件&#xff0c;能够自动执行ECU的功能测试和性能测试&#xff0c;确保其在不同工作条件下的可靠性和功能性。通过自动化测试系统&#xff0c;大大提高了测…

基于xilinx FPGA的GTX/GTH/GTY位置信息查看方式(如X0Y0在bank几)

目录 1 概述2 参考文档3 查看方式4查询总结&#xff1a; 1 概述 本文用于介绍如何查看xilinx fpga GTX得位置信息&#xff08;如X0Y0在哪个BANK/Quad&#xff09;。 2 参考文档 《ug476_7Series_Transceivers》 《pg156-ultrascale-pcie-gen3-en-us-4.4》 3 查看方式 通过…

linux——IPC 进程间通信

IPC 进程间通信 interprocess communicate IPC&#xff08;Inter-Process Communication&#xff09;&#xff0c;即进程间通信&#xff0c;其产生的原因主要可以归纳为以下几点&#xff1a; 进程空间的独立性 资源隔离&#xff1a;在现代操作系统中&#xff0c;每个进程都…

Hadoop-12-Hive 基本介绍 下载安装配置 MariaDB安装 3台云服务Hadoop集群 架构图 对比SQL HQL

章节内容 上一节我们完成了&#xff1a; Reduce JOIN 的介绍Reduce JOIN 的具体实现DriverMapperReducer运行测试 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&am…

独立开发者系列(18)——js的window对象

独立开发者&#xff0c;必然要面对JS代码&#xff0c;基本可以认为在脚本语言里面&#xff0c;JS门槛最低&#xff0c;正因为如此&#xff0c;JS也是最受欢迎的开发语言之一。JS的代码运行规律&#xff0c;按照代码模块执行&#xff0c;也就是<script></script> 每…

2024年上半年网络工程师下午真题及答案解析

试题一(20分) 某高校网络拓扑如下图所示&#xff0c;两校区核心&#xff08;CORE-1、CORE-2&#xff09;&#xff0c;出口防火墙&#xff08;NGFW-1、NGFW-2&#xff09;通过校区间光缆互联&#xff0c;配置OSPF实现全校路由收敛&#xff0c;两校区相距40km。两校区默认由本地…

「媒体邀约」苏州媒体宣传服务公司

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 苏州的媒体资源相当丰富&#xff0c;涵盖了报纸、电视、广…

postman请求访问:认证失败,无法访问系统资源

1、使用postman时&#xff0c;没有传入相应的token&#xff0c;就会出现这种情况&#xff0c;此时需要把token放进去 发现问题: { "msg": "请求访问&#xff1a;/getInfo&#xff0c;认证失败&#xff0c;无法访问系统资源", "code": 401 } 1…

金属制品行业企业数字化转型实践

金属制品行业总体上存在着企业数量多、规模小、管理流程复杂等特点。而在数字化应用方面&#xff0c;调查显示&#xff1a;金属制品行业企业信息化总体应用水平低&#xff0c;信息系统建设水平尚处于一般事务处理和简单信息管理阶段&#xff0c;“信息孤岛”问题严重。在信息化…

最新发布!MySQL 9.0 的向量 (VECTOR) 类型文档更新

7月1日&#xff0c;MySQL 9.0.0 创新版本, 8.4.1 LTS, 8.0.38 三版齐发。 发版当天安装包已经可以下载&#xff0c;我也在第一时间做了分享&#xff1a; MySQL 9.0.0 新鲜出炉&#xff01;支持向量类型 当时参考手册还未上线&#xff0c;这两天文档虽已上线&#xff0c;但似乎仍…

RPM包管理-rpm命令管理

1.RPM包命令原则 所有的rpm包都在光盘中 例&#xff1a;httpd-2.2.15-15.e16.centos.1.i686.rpm httpd 软件包名 2.2.15 软件版本 15 软件发布的次数 e16.centos 适合的Linux平台 i686 适合的硬件平台…

springboot酒店管理系统-计算机毕业设计源码93190

目 录 摘 要 1 绪论 1.1 选题背景与意义 1.2开发现状 1.3论文结构与章节安排 2 酒店管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析…

【计算机视觉】基于OpenCV的直线检测

直线检测原理 霍夫变换是图像处理必然接触到的一个算法&#xff0c;它通过一种投票算法检测具有特定形状的物体,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果&#xff0c;该方法可以进行圆&#xff0c;直线&#xff0c;椭…

docker安装ElasticSearchKibana

本文参考以下两篇文章 ✅ElasticSearch&Kibana 部署 云效 Thoughts 企业级知识库 (aliyun.com) docker安装ElasticSearch&Kibana - 飞书 安装elasticsearch 使用docker下载es&#xff1a; docker pull elasticsearch:8.13.0 挂载配置 创建挂在文件目录 mkdir…