Bootstrap 5 默认不支持 ripple 效果,需手动添加轻量 JS 和 CSS:为按钮添加 data-ripple 属性,JS 计算点击坐标并动态创建 ripple 元素,CSS 用 transform+opacity 实现动画,注意 position/overflow 设置及 touch/click 双事件适配。

Bootstrap 5 默认不带 ripple 效果,需手动添加
Bootstrap 5 官方移除了所有 JavaScript 组件的波纹(ripple)点击反馈,btn 类点击时只有 CSS :active 的瞬时变色,没有扩散圆点动画。如果你在 Bootstrap 4 项目里见过 ripple,那是依赖于 bootstrap.js 内置的 data-ripple 行为,但 5.x 已彻底剥离。
要实现它,得自己补一段轻量 JS + 对应 CSS。别引入第三方库(比如 waves.js),容易和 Bootstrap 的 btn 样式冲突,也增加 bundle 体积。
- 只对带
data-ripple属性的按钮生效,避免全局污染 - 用
getBoundingClientRect()算点击坐标,兼容按钮内含图标或文字换行 - 动画用
transform: scale()+opacity,不触发布局重排 - 加
pointer-events: none到 ripple 元素上,防止遮挡后续点击
如何给单个按钮添加 ripple 动画
在按钮 HTML 上加 data-ripple 属性即可,不需要额外 class:
<button type="button" class="btn btn-primary" data-ripple>提交</button>
对应的 JS 初始化逻辑建议放在 DOMContentLoaded 后执行,且只监听一次(避免重复绑定):
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('[data-ripple]').forEach(btn => {
btn.addEventListener('click', function(e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const ripple = document.createElement('span');
ripple.className = 'ripple';
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
this.appendChild(ripple);
// 动画结束后移除 DOM 节点
setTimeout(() => ripple.remove(), 600);
});
});
});- 注意:
e.clientX是视口坐标,必须减去rect.left/top才能准确定位到按钮内部 - 不要用
offsetWidth/Height计算中心点——按钮可能有 padding、border、字体缩放,中心点不等于视觉焦点 -
setTimeout时间设为 600ms,要和 CSS 中animation-duration严格一致
配套 CSS 必须包含的关键规则
这段 CSS 要确保加载在 Bootstrap 主样式之后,否则会被覆盖:
.btn[data-ripple] {
position: relative;
overflow: hidden;
}
<p>.btn[data-ripple] .ripple {
position: absolute;
border-radius: 100%;
background: rgba(255, 255, 255, 0.6);
width: 10px;
height: 10px;
opacity: 0;
transform: scale(1);
animation: ripple 0.6s linear;
pointer-events: none;
}</p><p>@keyframes ripple {
0% {
transform: scale(0);
opacity: 0.7;
}
100% {
transform: scale(4);
opacity: 0;
}
}-
overflow: hidden是必须的,否则 ripple 圆点会溢出按钮边界 - 动画结束时
opacity: 0和transform: scale(4)要同步,否则会出现“突然消失”或“卡顿”感 - 如果按钮是
btn-outline-*类型,把background改成rgba(0,0,0,0.1)更协调
移动端 click 延迟与 touch 事件适配
纯用 click 在 iOS Safari 或某些安卓 WebView 里会有约 300ms 延迟,导致 ripple 感觉滞后。应该同时监听 touchstart,并阻止默认行为:
btn.addEventListener('touchstart', function(e) {
e.preventDefault(); // 防止延迟
const touch = e.touches[0];
const rect = this.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
const ripple = document.createElement('span');
ripple.className = 'ripple';
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
this.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
});- 不能只监听
touchstart就完事——桌面端没 touch 事件,必须保留click回退 -
e.preventDefault()只在touchstart里加,不要在click里加,否则会破坏表单提交等默认行为 - 部分 Android 浏览器在 fastclick 关闭时仍可能触发双击,可加
touch-action: manipulation到按钮上进一步优化
实际部署时最容易被忽略的是 position: relative 和 overflow: hidden 的组合缺失——少了任一,ripple 就会飘到按钮外、或者撑开父容器。动画时间与 JS 移除节点的时间不同步,也会导致页面残留无用 span 元素。


















