加载状态按钮(提交中转圈/成功反馈三态切换源码)
原生 JS 实现按钮三态切换:空闲、加载中转圈禁用、成功对勾,防止重复提交,含重置按钮,适合表单提交与支付场景。
效果演示(真实可交互)
点击按钮模拟提交:加载 2 秒后显示成功
完整代码
HTML 结构:
<button id="ls-btn" class="ls-btn" data-state="idle">
<span class="ls-label">提交订单</span>
<span class="ls-spinner" aria-hidden="true"></span>
<span class="ls-check" aria-hidden="true">✓</span>
</button>
CSS 核心代码:
/* 转圈加载器 */
.ls-spinner {
display: none; width: 18px; height: 18px;
border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
border-radius: 50%; animation: ls-spin .7s linear infinite;
}
.ls-btn[data-state="loading"] .ls-spinner { display: inline-block; }
@keyframes ls-spin { to { transform: rotate(360deg); } }
/* 成功对勾:弹跳出现 */
.ls-check { display: none; transform: scale(0);
transition: transform .3s cubic-bezier(.34,1.56,.64,1); }
.ls-btn[data-state="success"] .ls-check {
display: inline-block; transform: scale(1);
}
JS 逻辑:
const btn = document.getElementById('ls-btn');
btn.addEventListener('click', () => {
if (btn.dataset.state === 'loading') return; // 防重复提交
btn.dataset.state = 'loading';
setTimeout(() => { btn.dataset.state = 'success'; }, 2000);
});
参数说明
| 参数 | 取值 | 说明 |
|---|---|---|
data-state | idle / loading / success | 三种状态的唯一数据源,CSS 靠它切换样式 |
.ls-spinner | 18px + .7s | 转圈大小与转速 |
setTimeout | 2000ms | 模拟请求耗时,实际接入时替换为 fetch/axios 回调 |
| 防重复提交 | if (loading) return | 加载中禁用点击,防止重复请求 |
.ls-check | cubic-bezier(.34,1.56,.64,1) | 回弹曲线让对勾更有活力 |
兼容性与性能
- 转圈加载器用 border 圆角 + 旋转动画实现,无需引入任何图标库,兼容所有现代浏览器
- 状态切换只操作一个
data-state属性和 display 属性,无重排重绘压力 - 动画只有 transform 旋转与缩放,全部由 GPU 合成,移动端流畅
- 建议配合真实请求使用:在
fetch的 finally 中切换状态,并处理失败态(可扩展data-state="error"显示重试文案)
常见问题
Q: 怎么加一个「失败重试」状态? 在 HTML 里加 <span class="ls-err">重试</span>,CSS 用 [data-state="error"] 控制显示,JS 里 catch 时把 state 设为 error 并恢复可点击即可。
Q: 防重复提交的原理是什么? 点击后立即把 state 切到 loading,同时通过 CSS pointer-events: none 禁用按钮;JS 里再加一道 if (state === 'loading') return 双重保险。
Q: 加载文案和转圈能并存吗? 可以,把转圈 <span> 放在按钮内并保留文字即可,本组件就是「提交订单 + 转圈」同时显示,宽度变化可用 min-width 固定。
Q: 能用在 Vue/React 里吗? 可以,把状态提升为组件 state 或响应式变量,用条件渲染替代 data-state 选择器,CSS 部分完全复用。
Q: 按钮宽度会因文案变化而跳动吗? 建议给按钮设置 min-width 并让三种文案长度接近;本组件将转圈和对勾内联在按钮内,宽度变化已被 min-width 吸收,不会跳动。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。