登录、注册、找回密码都会用到:点「获取验证码」,按钮开始倒数,数完才能再点。这版用 jQuery 1.9.1。倒计时跟框架没关系,换成原生 JS 或 Vue,无非事件绑法不同,流程还是这一套。
接到登录页时,按后面「接入后端接口」那节补请求就行。
使用的文件有哪些
文件 | 干嘛的 |
lib/jquery-1.9.1.min.js | jQuery,本地引 |
index.html | 「获取验证码」「重置」两个按钮 |
index.css | 常态、禁用态 |
index.js | 倒计时 |
jQuery 必须写在 index.js 前面,不然 $ 还没定义。index.js 包在 $(function () {}) 里,放 <head> 也会等 DOM 好了再绑,按钮还没出来也不怕。

主要是用到下面几个变量和方法,后面会反复碰到:
1、COUNT_DURATION:一共几秒
2、countDown:还剩几秒
3、timer:setTimeout 返回的计时器 ID
4、startCountDown()、tick()、resetCountDown():启动、每秒减一、复位
主要解决哪些问题
倒数的时候狂点也没用
一点按钮就 prop("disabled", true),浏览器不再给它派发 click。startCountDown 开头还有 if (timer) return,已经在倒数就直接回去。不锁的话每点一次新开一个倒计时,几个叠一起,秒数直接乱跳。
timer 得留着,不然停不下来
我第一次看也卡了一下,说白了吧:timer 存的是 setTimeout 返回的那个计时器 ID。resetCountDown 里靠 clearTimeout(timer) 把正在跑的倒计时停掉、按钮恢复。这个变量丢不得。
图省事写成 setTimeout(tick, 1000)、返回值不存,就再也找不到这个计时器——一开就停不下来,按钮永远置灰。这坑我见过不止一次。
顺带三个配合:
1、startCountDown 里 if (timer) return,防叠多个计时器。
2、每次 tick 都把新的 ID 存回 timer,因为每一跳都是新的 setTimeout。
3、resetCountDown 里 clearTimeout 之后把 timer 置 null,下次才开得动。
按钮文字走 5 → 4 → 3 → 2 → 1。数到头和点「重置」,都进同一个 resetCountDown():清计时器、文案改回「获取验证码」、按钮重新可点。
事件委托到 body,接后端方便
用 $("body").on("click", "#getCode", ...)。按钮是后来 JS 插进来的也能点(登录页经常是渲染出来的)。后端接口成功后再调 startCountDown(),不必把 ajax 写死在点击里。
如何嵌入到项目中
接入项目时:页面加上手机号输入框,业务脚本用下面这份完整示例,提示用 Layui 的 layer.msg。倒计时三个函数不用改,只是点击时先调后端,成功后再调用 startCountDown()。接口挂了按钮还是可点,能立刻重试;别一点击就锁死,那用户只能干瞪眼。
接入业务时要改的参数
1、秒数:本例 COUNT_DURATION = 5,方便立刻看到效果;业务里一般改成 60。
2、防刷:按钮前加图形验证码或滑块,别让人刷短信。
3、秒杀、抢购:别死磕每秒加 1000ms。记下点击时的时间戳,每次用 Date.now() 算剩余秒数更稳。

接入后端接口的完整示例
页面加手机号输入框,样式还用原来的 index.css。接口地址和 res.code 换成项目里的约定。


总结
1、点完 disabled 锁按钮,setTimeout 每秒减 1,减到 0 走 resetCountDown()。timer 一定留着,clearTimeout(timer) 全靠它找到正在跑的计时器,丢了就停不掉。
2、接到业务时再补:先调后端成功再倒计时、秒数改 60、加图形验证码或滑块;提示走 Layui。
用浏览器打开 index.html。点「获取验证码」应立刻变灰并显示 5,接着 4、3、2、1,再恢复「获取验证码」;倒数中途点「重置」应马上恢复可点。