HTML中toast本质是div+CSS动画+JS控制的非原生组件,需用fixed定位、transform/opacity动画、单例管理及animationend+setTimeout双保险销毁。

HTML 中用 toast 实现轻量提示,本质是 DOM + CSS 动画,不是原生 API
浏览器没有叫 toast 的内置元素或方法,所有“Toast 提示条”都是前端自己封装的 DOM 节点 + 定位 + 过渡动画。别被名字唬住,它就一个带 position: fixed 的 <div>,加几行 CSS 就能跑起来。
常见错误是直接套用框架组件(比如 Vue 的 $message 或 Ant Design 的 message.success()),但项目没引入对应 UI 库时,反而卡在“怎么单独拎出 toast 逻辑”。其实核心只需三块:容器、消息节点、显示/隐藏控制函数。
手写一个可复用的 showToast 函数,支持文字、时长、类型
用纯 JS 实现,不依赖任何库,兼容 Chrome/Firefox/Safari(IE 不建议考虑)。关键点在于:避免重复创建容器、自动销毁、防止多次点击堆叠。
- 只在第一次调用时创建全局容器
div#toast-container,插入到document.body底部 - 每次调用
showToast('操作成功', { type: 'success', duration: 2000 }),动态生成一个子<div class="toast">并追加进去 - 用
setTimeout控制自动消失,同时监听transitionend确保动画结束后再从 DOM 移除节点,避免残留 -
type参数仅用于添加类名(如toast--success),样式由 CSS 控制,方便换肤
function showToast(text, options = {}) {
const { type = 'info', duration = 3000 } = options;
const container = document.getElementById('toast-container') ||
(function() {
const el = document.createElement('div');
el.id = 'toast-container';
el.style.cssText = 'position: fixed; top: 16px; right: 16px; z-index: 9999; width: max-content;';
document.body.appendChild(el);
return el;
})();
const toast = document.createElement('div');
toast.className = `toast toast--${type}`;
toast.innerHTML = `<span>${text}</span>`;
container.appendChild(toast);
setTimeout(() => {
toast.classList.add('toast--hide');
toast.addEventListener('transitionend', () => {
if (toast.parentNode) toast.parentNode.removeChild(toast);
}, { once: true });
}, duration);
}
CSS 动画必须用 transform + opacity,别用 top 或 display
用 top 位移会触发重排(reflow),动画卡顿;用 display: none 无法过渡。正确做法是靠 transform: translateX 和 opacity 配合 transition。
立即学习“前端免费学习笔记(深入)”;
- 初始状态设为
opacity: 0; transform: translateX(120%); - 显示时加类
toast--show→opacity: 1; transform: translateX(0); - 隐藏时加类
toast--hide→opacity: 0; transform: translateX(120%); - 过渡写在基础类
.toast上:transition: opacity 0.3s ease, transform 0.3s ease;
注意:如果 Toast 需要支持多条堆叠(非覆盖),就得改用 flex 布局 + flex-direction: column-reverse,并给容器设 max-height 和 overflow: hidden,否则新消息会把旧消息顶出视口。
移动端点击穿透和焦点问题必须手动拦截
在 iOS Safari 或部分安卓 WebView 中,Toast 显示期间用户仍可能点到下层按钮,尤其当 Toast 没遮罩层时。这不是 bug,是浏览器默认行为——pointer-events: none 对 fixed 元素不总生效。
- 临时方案:Toast 显示时给
body加style.pointerEvents = 'none',隐藏后再恢复;但会锁死整个页面滚动 - 更稳妥做法:Toast 容器本身设
pointer-events: auto,且确保其z-index足够高(≥9999),同时避免使用touch-action: manipulation等干扰手势的属性 - 若 Toast 内含可交互元素(如“撤销”按钮),必须显式绑定事件,不能依赖事件委托——因为节点是动态创建、动态销毁的
真正难的不是让 Toast 弹出来,而是它在各种屏幕尺寸、各种用户操作节奏下都不抢焦、不卡顿、不堆叠、不漏点。这些细节藏在 CSS timing function、JS 节流、以及对 transitionend 事件的精准判断里。



















