前言
平常我们在使用一个移动端app的时候,很多场景下都要用到验证码相关的功能,常见的就是一个input输入框加些样式,然后直接把收到的验证码填入即可。
为了提高用户体验,这次我们就来手动实现一个好看一些的验证码输入界面,效果图如下:
实现步骤如下:
创建元素
<div class="container">
<span>输入验证码</span>
<span>验证码已发送到<span class="phone">+86 191*******121</span></span>
<div class="codeBox">
<div class="codeItem active"></div>
<div class="codeItem"></div>
<div class="codeItem"></div>
<div class="codeItem"></div>
<input type="text" class="codeInput" maxlength="4" />
</div>
<a>重新获取验证码</a>
</div>
定义样式
<style>
* {
box-sizing: border-box;
}
a {
text-decoration: none;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
padding-top: calc(150 / 3.75 * 1vw);
}
.codeBox {
width: calc(250 / 3.75 * 1vw);
display: flex;
justify-content: space-between;
position: relative;
margin-bottom: calc(50 / 3.75 * 1vw);
}
.codeItem {
width: calc(50 / 3.75 * 1vw);
height: calc(50 / 3.75 * 1vw);
font-size: calc(24 / 3.75 * 1vw);
text-align: center;
line-height: calc(50 / 3.75 * 1vw);
border: calc(1 / 3.75 * 1vw) solid #ccc;
border-radius: calc(10 / 3.75 * 1vw);
transition: all 0.2s linear;
position: relative;
}
.codeInput {
width: 100%;
height: 100%;
position: absolute;
opacity: 0;
}
.container span:nth-child(1) {
font-size: calc(20 / 3.75 * 1vw);
margin-bottom: calc(10 / 3.75 * 1vw);
}
.container span:nth-child(2) {
font-size: calc(12 / 3.75 * 1vw);
margin-bottom: calc(30 / 3.75 * 1vw);
}
.container :last-child ,.container span .phone{
font-size: calc(12 / 3.75 * 1vw);
color: rgb(105, 161, 230);
}
.active {
border: calc(3 / 3.75 * 1vw) solid rgb(105, 161, 230);
}
/* 闪烁光标的样式 */
.codeItem.active::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(2 / 3.75 * 1vw);
/* 光标的宽度 */
height: calc(24 / 3.75 * 1vw);
/* 光标的高度 */
background-color: black;
animation: blink 1s step-end infinite;
}
/* 闪烁动画 */
@keyframes blink {
50% {
opacity: 0;
}
}
</style>
现在完成了静态模板,下面就实现交互逻辑
实现的思路:
codeBox中的4个div模拟的是4个输入框,真实的input输入框其实是隐藏起来的,这样方便做样式处理
在input输入框中输入内容时,通过计算给对应位置的div加激活样式,并把input中的内容展示到这些div的内容中
1、获取dom元素
首先要获取这4个div元素和输入框元素,并且加载页面自动聚焦到inputBox
//4个div输入框元素
const codeItems = document.querySelectorAll(".codeItem");
//真正的输入框元素
const inputBox = document.querySelector(".codeInput");
//进入页面输入框就自动聚焦
inputBox.focus();
2、绑定事件
要实现输入后的效果,输入框事件少不了,给inputBox添加input事件
inputBox.addEventListener("input", (e) => {
//切换激活样式方法
changeActiveItem();
//更新div输入框的内容
updateCodeItem();
//输入完成发送校验请求
if (inputBox.value.length === 4) {
//sendCode();
}
});
这里的codeItems是一个NodeList,原型链上有forEach方法
function changeActiveItem() {
//先清除带有active样式的div输入框
codeItems.forEach((item) => {
item.classList.remove("active");
});
//根据输入框里内容的长度length,来计算当前所在div的索引位置,并加上样式
//首先获取输入框字符串长度
const inputValue = inputBox.value.length;
// 如果输入框没内容,就默认给第一个div加active样式
if (inputValue === 0) {
codeItems[0].classList.add("active");
}
//判断字符串是否为空并且<4个字符,否则,删空内容或中文输入的时候索引会超出,导致报错
if (inputValue && inputValue < 4) {
codeItems[inputValue].classList.add("active");
}
}
function updateCodeItem() {
// 拿到实际input输入框的内容
const arrCode = inputBox.value;
// 遍历每一个div输入框元素,根据索引给元素的innerText赋值
codeItems.forEach((item, index) => {
// 一开始输入,其他验证框就会变成undefined,因为arrCode此时长度没到4,遍历后面的索引会超出范围
arrCode[index] ? item.innerText = arrCode[index] : item.innerText = "";
});
}
出现的问题:
1、能输入汉字
到这里似乎没啥问题了,可以正常输入,样式也会相应地变化了,但是有个小问题,验证码框里居然能输入汉字,接下来继续进行改造完善。
2、包含数字和字母
既然要求不能输入汉字,那我直接在input的属性里加上 type="number"就行了嘛。但是有些验证码要数字和字母结合咋办呢。
解决:要对input输入的汉字进行实时过滤,可以用正则表达式+replace方法。
回到input输入框的input事件中,加上这样一行,这样就基本上完成了页面逻辑了,后续可以再进行逻辑优化。
inputBox.addEventListener("input", (e) => {
//把输入的文本替换成空字符串,否则结束中文输入时,输入框内容会有汉字
e.target.value = e.target.value.replace(/[^\x00-\xff]/g, "");
//执行代码
})