验证码这玩意儿,验证前端做起来其实比后端简单得多。码代码写后端要生成图片、验证存session、码代码写校验,验证前端只需要把用户输入的码代码写数字跟随机生成的那串比对一下就行。但就这么点事,验证很多人写出来一堆bug——比如刷新后验证码没变,码代码写或者大小写没统一导致永远验证失败。验证
我见过最离谱的码代码写写法是直接在HTML里写死一个“1234”,然后JS里判断输入是验证否等于“1234”。这跟没写一样,码代码写爬虫都懒得绕。验证正经做法是码代码写每次页面加载或点击刷新时,用Math.random()生成一个4位随机数,验证存到一个变量里,再渲染到页面上。
先看最基础的版本:
<div id="captcha" style="font-size:24px; letter-spacing:8px; background:#eee; padding:10px; display:inline-block;"></div> <input type="text" id="userInput" placeholder="输入验证码"> <button onclick="validate()">提交</button> <button onclick="refresh()">刷新</button> <script> let currentCode = ''; function generateCode() { const chars = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789'; let code = ''; for (let i = 0; i < 4; i++) { code += chars[Math.floor(Math.random() * chars.length)]; } return code; } function refresh() { currentCode = generateCode(); document.getElementById('captcha').textContent = currentCode; } function validate() { const input = document.getElementById('userInput').value.trim().toUpperCase(); if (input === currentCode) { alert('验证通过'); } else { alert('验证码错误'); refresh(); } } refresh(); </script> 这段代码能跑,但有个细节容易翻车:用户输入小写字母时,toUpperCase()必须加,否则“a”和“A”永远对不上。我一开始就踩过这个坑,测试时自己输小写,死活通不过,还以为是随机数生成有问题。
再进一步,很多人想让验证码看起来“像图片”,其实用CSS就能模拟。把字符间距拉大、加个旋转、背景加几条干扰线——不用canvas,直接border和伪元素就行。比如:
#captcha { font-family: 'Courier New', monospace; font-weight: bold; color: #333; text-shadow: 1px 1px 2px rgba(0,0,0,0.2); transform: rotate(-2deg); user-select: none; } 注意user-select: none,不然用户直接双击选中复制粘贴,验证码就失去意义了。虽然前端验证码本来也防不住脚本,但至少别让手动操作太轻松。
还有一种常见需求:点击验证码本身刷新。把refresh函数绑到div的onclick上就行:
document.getElementById('captcha').onclick = refresh; 这里有个小坑——如果用户点太快,连续触发refresh,currentCode会被覆盖多次,但页面只显示最后一次,没问题。真正的问题在于,如果验证码区域有文字被选中,点击时可能触发选中而不是click。加个user-select: none就解决了。
实际项目里,我习惯把验证码逻辑封装成一个对象,避免全局变量污染:
const Captcha = { code: '', chars: 'ABCDEFGHJKMNPQRSTUVWXYZ23456789', generate() { let c = ''; for (let i = 0; i < 4; i++) { c += this.chars[Math.floor(Math.random() * this.chars.length)]; } this.code = c; return c; }, render(el) { el.textContent = this.generate(); }, check(input) { return input.trim().toUpperCase() === this.code; } }; 用的时候:
Captcha.render(document.getElementById('captcha')); // 提交时 if (Captcha.check(userInput.value)) { ... } 这样刷新和校验都走同一个对象,逻辑清晰,也不怕变量名冲突。唯一要注意的是,Captcha.code是明文存在内存里的,打开控制台就能看到。但前端验证码本来就是这样——它防的是普通用户误操作,不是黑客。真要安全,必须后端生成、后端校验,前端只负责传用户输入。
最后说个实际观察:很多教程把验证码写成canvas画图,代码量翻三倍,效果还不如纯CSS加随机旋转。除非你要做干扰线、噪点、扭曲变形,否则没必要上canvas。简单点,维护起来也轻松。