一般情况下,如果要实现密码输入框,采用浏览器原生的密码输入框是很好的选择。比如登录界面,使用浏览器原生的密码输入框,用户就可以使用浏览器自身的‘’记住密码“功能,不用每次登录都需要手动输入账号密码。但是呢!有某些情况下,只需要密码框的密码显示/隐藏功能。如果使用浏览器原生的密码框,“记住密码”功能的弹窗,就会造成很大的不便。所以,在这种情况下,就需要模拟实现密码框的功能了。
在实现密码框之前,首先要了解密码框的功能。
密码框的功能有:
首先,密码框能像普通的文本输入框一样,进行密码的输入修改。此外,用户输入的密码不能明码显示出来,要转化成特殊字符(●
)显示。
要实现这一点,需要做两个步骤:
input
事件,用户每输入一个字符,都要将用户输入的字符存储起来,并将其转化为特殊字符(●
)在输入框中显示出来。(存储起来的字符才是真正的密码)密码框还需要一个密码显示隐藏的按钮。用户点击这个按钮,可以控制密码框中的密码是明码显示的还是隐藏的。
// 密码输入框组件 class PasswordInput { /** * 构造函数 * @param {String} constainer_selector 密码输入框父(容器)元素的样式选择器 * @param {Function} toggleCallback 密码显示隐藏回调函数 */ constructor(constainer_selector, toggleCallback) { 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(); // 添加 input 事件 this._inputElem.addEventListener('input', this.inputHandle); } if (this._btnToggleElem) { // 添加显示隐藏按钮点击事件 this._btnToggleElem.addEventListener('click', this.btnToggleElemClick); } } /** * input 初始化 */ initInput() { this._inputElem.status = 'hide'; // 初始时,默认隐藏状态 this._inputElem.pwdValue = ''; // pwdValue 属性,用于存储真正的密码 this._inputElem.type = 'text'; // 强制 input type 为 text, 防止用户设置 password 类型从而造成影响 if (this._inputElem.value) { // 如果存在默认密码,则将默认密码存入 pwdValue,并渲染一次密码 this._inputElem.pwdValue = this._inputElem.value; this.toggleRender(); } } // input 事件 inputHandle = () => { const val = this._inputElem.value; let newPwd = ''; // 存储新的真正密码 let oldPwd = this._inputElem.pwdValue || ''; // 获取存储的真正密码,将其定为旧密码 const cursorIndex = this._inputElem.selectionStart; // 获取光标在输入框中的位置 if (this._inputElem.status == 'hide') { // 当密码需要隐藏时,将密码转为*,真正的密码为 pwdValue if (oldPwd && oldPwd.length >= val.length) { // 旧的真实密码存在,且其字符串长度大于输入框的字符串长度,说明用户进行删除操作 const stop = oldPwd.length - val.length + cursorIndex; // 用户删除的字符串长度加光标的当前的位置,计算得出删除字符串的最后一个字符的位置 const del_string = oldPwd.substring(cursorIndex, stop); // 获取用户删除的字符串 newPwd = oldPwd.replace(del_string, ''); // 将旧的真实密码中对应的删除字符串替换为'',实现对真实密码的删除操作 } else { const reg = /[^●]/.exec(val); // 获取虚假密码中新增的密码字符 newPwd = this.insertStr(oldPwd, reg.index, reg[0]); // 将用户新输入的字符插入旧的真实密码 this.cursorMove(this._inputElem, reg.index + 1); // 设置光标的位置 } } else { // 当不需要隐藏密码时,仍需要将密码存入 pwdValue newPwd = 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) { //兼容IE var rng = elem.createTextRange(); rng.move('character', spos); rng.select(); } } }
使用方法:
<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"
。
组件:
<template> <div class="password-input"> <input type="text" :value="hideValue" @input="input" ref='password-input' :placeholder="placeholder" > <div class="btn-show" @click="isShow = !isShow"> <img v-if="isShow" src="@/assets/pwd-show.png" /> <img v-else src="@/assets/pwd-hide.png" /> </div> </div> </template> <script> export default { name: 'password-input', props: { modelVal: String, placeholder: { type: String, default: '请输入' } }, model: { prop: 'modelVal', //指向props的参数名 event: 'input' //事件名称 }, data() { return { isShow: false, hideValue: '' }; }, 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, '●'); } }, input() { let new_pwd = ''; // 存储新的真实密码 let old_pwd = this.modelVal || ''; // 获取旧的真实密码 const pwd_input_elem = this.$refs['password-input']; // 获取密码输入框DOM let 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) { //兼容IE var rng = elem.createTextRange(); rng.move('character', spos); rng.select(); } } } }; </script> <style lang="less" scoped> .password-input { position: relative; 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>
组件:
<template> <div class="password-input"> <input type="text" :value="hideValue" @input="input" ref='password-input' :placeholder="placeholder" > <div class="btn-show" @click="isShow = !isShow"> <img v-if="isShow" src="@/assets/pwd-show.png" /> <img v-else src="@/assets/pwd-hide.png" /> </div> </div> </template> <script> export default { name: 'password-input', props: { modelVal: String, placeholder: { type: String, default: '请输入' } }, emits: ['update:modelVal'], data() { return { isShow: false, hideValue: '' }; }, 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, '●'); } }, input() { let new_pwd = ''; // 存储新的真实密码 let old_pwd = this.modelVal || ''; // 获取旧的真实密码 const pwd_input_elem = this.$refs['password-input']; // 获取密码输入框DOM let 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('update:modelVal', 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) { //兼容IE var rng = elem.createTextRange(); rng.move('character', spos); rng.select(); } } } }; </script> <style lang="less" scoped> .password-input { position: relative; box-sizing: border-box; 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>
使用方法:
<TheInputPassward v-model:modelVal="val" />