变更记录:

  • 增加中文输入限制
  • 增加粘贴限制

一般情况下,如果要实现密码输入框,采用浏览器原生的密码输入框是很好的选择。比如登录界面,使用浏览器原生的密码输入框,用户就可以使用浏览器自身的‘’记住密码“功能,不用每次登录都需要手动输入账号密码。但是呢!有某些情况下,只需要密码框的密码显示/隐藏功能。如果使用浏览器原生的密码框,“记住密码”功能的弹窗,就会造成很大的不便。所以,在这种情况下,就需要模拟实现密码框的功能了。

密码框的功能解析

在实现密码框之前,首先要了解密码框的功能。

密码框的功能有:

  • 文本输入功能
  • 密码显示隐藏功能

文本输入功能

首先,密码框能像普通的文本输入框一样,进行密码的输入修改。此外,用户输入的密码不能明码显示出来,要转化成特殊字符()显示。

要实现这一点,需要做两个步骤:

  1. 使用普通的文本输入框来进行密码输入
  2. 给文本输入框添加 input 事件,用户每输入一个字符,都要将用户输入的字符存储起来,并将其转化为特殊字符()在输入框中显示出来。(存储起来的字符才是真正的密码)

密码显示隐藏功能

密码框还需要一个密码显示隐藏的按钮。用户点击这个按钮,可以控制密码框中的密码是明码显示的还是隐藏的。

限制中文的输入

原生的密码输入框是限制中文输入法的。所以,我们也要对中文做限制。
这里通过两种途径来实现中文输入的限制:

  • 限制中文输入法,只允许英文输入法。
  • 将用户输入的中文字符替换为空
限制中文输入法

ime-mode:

  • 用途:CSS属性,用于设置或检索是否允许用户激活输入中文,韩文,日文等的输入法(IME)状态。
  • 可选值:
  • auto不改变输入法状态,此为预设值。
  • normal输入法设为一般状态,使用者可在自订样式表中盖过网页的设定。Internet Explorer不支援此值。active输入法设为启用状态。除非使用者刻意关闭、否则此文字栏位将使用输入法工具。Linux不支援此值。
  • inactive输入法设为关闭状态,但使用者仍可另行启用。Linux不支援此值。
  • disabled输入法设为停用状态,在此栏位中使用者亦无法将其启用。
  • 用法:
<input type="text" name="name" value="initial value" style="ime-mode: disabled">

显然,借助ime-mode,可以轻松实现中文输入的限制。

然而,在 Chrome 浏览器中,ime-mode 属性是失效的。因为:

“ime-mode”是在某些浏览器中实现的一个属性,这是有问题的,并且被这个规范淘汰了。

浏览器兼容性

Feature Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support No support (Yes) 3.0 (1.9) 5.0 1 No support No support
Feature Android Edge Firefox Mobile (Gecko) IE Phone Opera Mobile Safari Mobile
Basic support ? (Yes) ? ? ? ?

所以,我们需要一个另外一种限制中文输入的方法。我的解决方案是:将用户输入的中文字符替换为空

将用户输入的中文字符替换为空

首先,我们先看一个动图:

可以看出,在进行中文输入的过程中,所按的字符会同步输入到输入框中。这样就很难区分开哪些字符是进行中文输入是产生的字符了。区分不出来,就不能有效的限制中文输入,极有可能照成“误杀”。

怎么解决呢?

HTML 提供了几个这样的事件:

compositionstart:(中文输入开始)

文本合成系统如 input method editor(即输入法编辑器)开始新的输入合成时会触发 compositionstart 事件。

例如,当用户使用拼音输入法开始输入汉字时,这个事件就会被触发。

compositionupdate:(中文输入中)

compositionupdate 事件触发于字符被输入到一段文字的时候(这些可见字符的输入可能需要一连串的键盘操作、语音识别或者点击输入法的备选词)

compositionend:(中文输入结束)

当文本段落的组成完成或取消时, compositionend 事件将被触发 (具有特殊字符的触发, 需要一系列键和其他输入, 如语音识别或移动中的字词建议)。

很幸运,借助这三个事件,我们可以清楚的知道用户什么时候进行中文输入,什么时候结束中文输入。

因此,我们的解决思路可以这样:

  • 用户不进行中文输入时,用户每输入一个字符,都进行处理;
  • 用户开始进行中文输入后,暂停处理操作;
  • 等用户完成中文输入后,立即替换用户输入的中文字符。

这样我们就可以实现:将用户输入的中文字符替换为空

密码框的实现

原生 JavaScript

// 密码输入框组件
class PasswordInput {/*** 构造函数* @param {String} constainer_selector 密码输入框父(容器)元素的样式选择器* @param {Function} toggleCallback  密码显示隐藏回调函数*/constructor(constainer_selector, toggleCallback) {this.compositionStatue = false; // 中文输入的标记符:true 为正在进行中文输入this.compositionStartCursorIndex = 0; // 记录进行中文输入时光标的位置this._containerElem = document.querySelector(constainer_selector); // 密码框父(容器)元素this._inputElem = document.querySelector(`${constainer_selector} .password-input`); // 密码框 input 本身this._btnToggleElem = document.querySelector(`${constainer_selector} .btn-toggle`); // 密码显示隐藏按钮元素/*** 密码显示隐藏回调函数* @param {Element} 按钮本身* @param {String} 显示隐藏标识:show / hide*/this._toggleCallback = toggleCallback;if (this._inputElem) {this.initInput();}if (this._btnToggleElem) {// 添加显示隐藏按钮点击事件this._btnToggleElem.addEventListener('click', this.btnToggleElemClick);this._btnToggleElem.addEventListener('paste', function() {return false;});}}/*** input 初始化*/initInput() {this._inputElem.status = 'hide'; // 初始时,默认隐藏状态this._inputElem.pwdValue = ''; // pwdValue 属性,用于存储真正的密码this._inputElem.type = 'text'; // 强制 input type 为 text, 防止用户设置 password 类型从而造成影响this._inputElem.setAttribute('ime-mode', 'disabled');if (this._inputElem.value) {// 如果存在默认密码,则将默认密码存入 pwdValue,并渲染一次密码this._inputElem.pwdValue = this._inputElem.value;this.toggleRender();}// 开始进行中文输入时触发的事件this._inputElem.addEventListener('compositionstart', () => {this.compositionStartCursorIndex = this._inputElem.selectionStart; // 记录进行中文输入时光标的位置this.compositionStatue = true;});// 中文输入结束后触发的事件this._inputElem.addEventListener('compositionend', () => {this.limitCN();this.compositionStatue = false;this.compositionLength = 0;});// input 事件this._inputElem.addEventListener('input', () => {if (this.compositionStatue) {// 进行中文输入时不执行 inputHandle 函数return false;}this.inputHandle();});// 禁止粘贴this._inputElem.addEventListener('paste', (e) => {e.preventDefault();return false;});}// input 事件inputHandle = () => {const val = this._inputElem.value;let newPwd = ''; // 存储新的真正密码let oldPwd = this._inputElem.pwdValue || ''; // 获取存储的真正密码,将其定为旧密码const cursorIndex = this._inputElem.selectionStart; // 获取光标在输入框中的位置if (this._inputElem.status == 'hide') {// 当密码需要隐藏时,将密码转为*,真正的密码为 pwdValueif (oldPwd && oldPwd.length > val.length) {// 旧的真实密码存在,且其字符串长度大于输入框的字符串长度,说明用户进行删除操作const compositionStatue = oldPwd.length - val.length + cursorIndex; // 用户删除的字符串长度加光标的当前的位置,计算得出删除字符串的最后一个字符的位置const del_string = oldPwd.substring(cursorIndex, compositionStatue); // 获取用户删除的字符串newPwd = oldPwd.replace(del_string, ''); // 将旧的真实密码中对应的删除字符串替换为'',实现对真实密码的删除操作} else {const reg = /[^•]/.exec(val); // 获取虚假密码中新增的密码字符if (reg) {// 如果存在新增密码字符,则进行输入输入处理newPwd = this.insertStr(oldPwd, reg.index, reg[0]); // 将用户新输入的字符插入旧的真实密码this.cursorMove(this._inputElem, reg.index + 1); // 设置光标的位置} else {// 如果不存在新增密码字符,不做改变newPwd = oldPwd;}}} else {// 当不需要隐藏密码时,仍需要将密码存入 pwdValuenewPwd = val;}this._inputElem.pwdValue = newPwd;this.toggleRender();};// 密码隐藏显示切换按钮 click 事件btnToggleElemClick = () => {if (this._inputElem.status == 'hide') {this._inputElem.status = 'show';} else {this._inputElem.status = 'hide';}this._toggleCallback(this._btnToggleElem, this._inputElem.status); /// 执行显示隐藏的回调函数this.toggleRender();};// 密码显示/隐藏切换时,对input value 的处理的渲染函数toggleRender() {const val = this._inputElem.value;if (this._inputElem.status == 'hide') {const replaceVal = val.replace(/[^•]/g, '•');this._inputElem.value = replaceVal;} else {this._inputElem.value = this._inputElem.pwdValue;}}/*** 根据位置在字符串中插入字符串* @params soure 原字符串* @params start 位置* @params newStr 要插入的字符串*/insertStr(soure, start, newStr) {return soure.slice(0, start) + newStr + soure.slice(start);}/*** 控制光标的位置*/cursorMove(elem, spos) {// spos 光标的位置 -1为最后一位if (spos < 0) spos = elem.value.length;if (elem.setSelectionRange) {//兼容火狐,谷歌setTimeout(function() {elem.setSelectionRange(spos, spos);elem.focus();}, 0);} else if (elem.createTextRange) {//兼容IEvar rng = elem.createTextRange();rng.move('character', spos);rng.select();}}// 限制中文输入limitCN() {let val = this._inputElem.value; // 获取输入框中的值、val = val.replace(/[^\x00-\x80•]/gi, ''); // 把所有双字节字符替换为空(排除•)this._inputElem.value = val;this.cursorMove(this._inputElem, this.compositionStartCursorIndex); // 将光标重置为中文输入前的位置}
}

使用方法:

<body><div class="pwd"><input type="password" class="password-input" value="1111" /><button class="btn-toggle" type="button">切换</button></div><script src="PasswordInput.js"></script><script>window.onload = function() {new PasswordInput('.pwd', (e, status) => {console.log(e);console.log(status);});};</script>
</body>

注意,input 一定要添加class="password-input", 切换按钮一定要添加class="btn-toggle"

Vue 2.x

组件:

<template><div class="password-input" :style="{ width: width }"><inputstyle="ime-mode: disabled":value="hideValue"@compositionstart="compositionstartHandle"@compositionend="compositionendHandle"@input="inputHandel"@paste.capture.prevent="pasteHandle"ref="password-input":placeholder="placeholder"/><div class="btn-show" @click="isShow = !isShow"><imgv-if="isShow"src="@/modules/case-show/modules/input-password/assets/pwd-show.png"/><imgv-elsesrc="@/modules/case-show/modules/input-password/assets/pwd-hide.png"/></div></div>
</template><script>
export default {name: 'password-input',props: {modelVal: String,placeholder: {type: String,default: '请输入'},width: {type: String,default: '300px'}},model: {prop: 'modelVal', //指向props的参数名event: 'input' //事件名称},data() {return {isShow: false,hideValue: '',compositionStatue: false,compositionStartCursorIndex: 0};},watch: {isShow: function() {this.render(this.modelVal);},modelVal: function(val) {this.render(val);}},methods: {render(val) {if (this.isShow) {this.hideValue = val;} else {this.hideValue = val.replace(/[^•]/g, '•');}},compositionstartHandle() {this.compositionStartCursorIndex = this.$refs['password-input'].selectionStart; // 记录进行中文输入时光标的位置this.compositionStatue = true;},compositionendHandle() {this.limitCN();this.compositionStatue = false;this.compositionLength = 0;},inputHandel() {if (this.compositionStatue) {// 进行中文输入时不执行 inputHandle 函数return false;}this.formatPassword();},pasteHandle() {return false;},formatPassword() {let new_pwd = ''; // 存储新的真实密码let old_pwd = this.modelVal || ''; // 获取旧的真实密码const pwd_input_elem = this.$refs['password-input']; // 获取密码输入框DOMlet val = this.$refs['password-input'].value; // 获取输入框中的值const cursorIndex = pwd_input_elem.selectionStart; // 获取光标在输入框中的位置if (this.isShow) {// 明码显示,不做处理new_pwd = val;} else {// 隐藏密码if (old_pwd && old_pwd.length > val.length) {// 旧的真实密码存在,且其字符串长度大于输入框的字符串长度,说明用户进行删除操作const stop = old_pwd.length - val.length + cursorIndex; // 用户删除的字符串长度加光标的当前的位置,计算得出删除字符串的最后一个字符的位置const del_string = old_pwd.substring(cursorIndex, stop); // 获取用户删除的字符串new_pwd = old_pwd.replace(del_string, ''); // 将旧的真实密码中对应的删除字符串替换为'',实现对真实密码的删除操作} else {const reg = /[^•]/.exec(val); // 获取虚假密码中新增的密码字符new_pwd = this.insertStr(old_pwd, reg.index, reg[0]); // 将用户新输入的字符插入旧的真实密码this.cursorMove(pwd_input_elem, reg.index + 1); // 设置光标的位置}}this.$emit('input', new_pwd);},/*** 根据位置在字符串中插入字符串* @params soure 原字符串* @params start 位置* @params newStr 要插入的字符串*/insertStr(soure, start, newStr) {return soure.slice(0, start) + newStr + soure.slice(start);},/*** 控制光标的位置*/cursorMove(elem, spos) {// spos 光标的位置 -1为最后一位if (spos < 0) spos = elem.value.length;if (elem.setSelectionRange) {//兼容火狐,谷歌setTimeout(function() {elem.setSelectionRange(spos, spos);elem.focus();}, 0);} else if (elem.createTextRange) {//兼容IEvar rng = elem.createTextRange();rng.move('character', spos);rng.select();}},// 限制中文输入limitCN() {let val = this.$refs['password-input'].value; // 获取输入框中的值// eslint-disable-next-line no-control-regexval = val.replace(/[^\x00-\x80•]/gi, '');this.$refs['password-input'].value = val;this.cursorMove(this.$refs['password-input'],this.compositionStartCursorIndex); // 将光标重置为中文输入前的位置}}
};
</script><style lang="less" scoped>
.password-input {position: relative;margin: 0 auto;input {height: 28px;width: 100%;line-height: 28px;padding-left: 10px;padding-right: 30px;box-sizing: border-box;}.btn-show {position: absolute;top: 50%;right: 0;transform: translateY(-50%);display: flex;height: 100%;width: 30px;align-items: center;justify-content: flex-start;cursor: pointer;img {width: 20px;height: 20px;}}
}
</style>

使用方法:

<InputPwd v-model="val"></InputPwd>

参考文章

  • input 事件兼容处理以及中文输入法优化
  • input禁止中文输入
  • compositionstart
  • compositionend
  • ime 样式 | ime-mode

密码输入框组件的实现相关推荐

  1. ueditor上传组件显示乱码_最全面的移动端 UI组件设计详解:中篇

    上一期给大家讲解了<最全面的移动端UI组件设计详解:上篇>,主要分享了:布局组件和导航组件2个部分:这次给大家带来:基础组件.表单组件和反馈组件详解,希望你在设计APP.小程序.H5页面中 ...

  2. pythonqt库_Python QT组件库qtwidgets的使用

    虽然Qt提供了不少现成的组件,但是在Python中使用PyQt5或PySide2进行图形界面程序开发的过程,还是免不了要根据自己的需求组合一些小部件以形成新的自定义组件. 最近州的先生在写一个桌面图形 ...

  3. 三十六、深入Vue.js组件Component(上篇)

    @Author:Runsen @Date:2020/6/15 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

  4. python qt 拖拽组件使用方法_Python QT组件库qtwidgets的使用

    虽然Qt提供了不少现成的组件,但是在Python中使用PyQt5或PySide2进行图形界面程序开发的过程,还是免不了要根据自己的需求组合一些小部件以形成新的自定义组件. 最近州的先生在写一个桌面图形 ...

  5. 解决 Element的el-input 密码输入框浏览器自动填充账号密码问题

    问题描述 通常情况下,浏览器会默认将已保存的账号密码 填充到 input    type 值为password的输入框内,如果在登录页面,这当然是非常好的,自动填充密码可以节约时间,提高良好的使用体验 ...

  6. python开发qt_qt开发python

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 不带参数创建的按钮控件如果我们不带字符串参数给qpushbutton(),创建的 ...

  7. JAVA实现简单的登录界面

    我本来是学C++的,然后课程上老师要求做一个登陆界面,用C++实现不限时,然后就选择了JAVA,从零开始自学JAVA.好在网上很多大佬都写了如何用JAVA编写登陆界面的博客,写得很详细,使得我第一次接 ...

  8. html密码框ml表单文本框,表单组件 PasswordInput 密码输入框 - 闪电教程JSRUN

    PasswordInput 密码输入框 介绍 带网格的输入框组件,可以用于输入支付密码.短信验证码等,通常与数字键盘组件配合使用 引入 import Vue from 'vue'; import { ...

  9. 有赞组件vant密码输入框input在微信小程序里隐藏显示密码有问题

    <van-fieldvalue="{{ pwd}}"placeholder="请输入密码"border="{{ false }}"cl ...

最新文章

  1. 数学知识--Levenberg-Marquardt算法浅谈
  2. BH60绝对位置旋转编码器编程资料
  3. java多线程机制2(安全问题)
  4. 将十六进制的字符串转换成整数
  5. 剑指Offer 二维数组中的查找
  6. 【计算机就业-算法工程师】校招想去互联网公司担任算法工程师该怎么准备
  7. linux中paste的用法,在Linux中使用Paste命令来合并行,包括使用Paste命令技巧及注意事项...
  8. maven学习- 私服nexus搭建
  9. PLSQL Split分割字符串
  10. 数据分析融入至BI工具的新思路
  11. 前端学习/资源/工具网站
  12. PostgreSQL extra_float_digits——控制浮点数精度
  13. ASP.NET(C#版) FileUpload控件
  14. 确定你的台式计算机支持的内存类型,如何区分内存类型及查看内存的兼容性
  15. Jmeter自定义函数开发-------输入参数被分割
  16. php英文验证码,php 中英文验证码程序
  17. 2021最新微信域名检测后屏蔽举报源码
  18. Java实现一个学生成绩管理系统,要求存储学生信息并进行增删改查操作。
  19. python判定固定时长固定频率的音频是否连续
  20. spinal HDL - 01 - 环境搭建与Scala编程指南

热门文章

  1. 羊皮卷之八:今天我要加倍重视自己的价值
  2. 2018 CCSP 杭州之行
  3. 【字节跳动实习面经(测试开发岗 二面)希望渺茫】
  4. 一百万个 WebSockets 和 Go
  5. 微表面模型GGX/Trowbridge-Reitz概率密度函数的推导
  6. 校准曲线的绘制的小技巧
  7. Java中线程超详细版本
  8. 线性代数学习笔记——第八十九讲——二次曲面的标准方程与图形(2)
  9. 【Python + Zotero】近乎全自动的完美 bib 文件生成
  10. 问世到现在电子计算机的性能,全国计算机一级笔试模拟题