如何使Popup Div可堆叠?非前端框架下多弹窗去重叠方案
弹窗堆叠排列实现方案
问题
通过函数向主div插入弹窗,单次调用功能正常,但多次调用后所有弹窗相互重叠,需要实现多个弹窗依次上下堆叠显示,且不依赖任何前端框架。
解决方案
问题根源
当前所有弹窗使用position: absolute且固定top: 25px,导致所有弹窗定位在同一位置,出现重叠。需要动态计算每个新弹窗的垂直偏移量,同时处理弹窗关闭后的位置重排。
具体修改
- 动态计算弹窗位置
在插入新弹窗前,统计当前已激活的弹窗数量,新弹窗的top值为初始top值 + 已激活数量 × (弹窗高度+间距),确保每个新弹窗都在上一个弹窗下方显示。 - 精准操作当前弹窗
原代码使用document.querySelector(".toast")会选中页面中第一个弹窗,改为获取刚插入的弹窗元素(wrapper.lastElementChild),确保操作对象是当前新生成的弹窗。 - 弹窗关闭后重排位置
当某个弹窗关闭并移除后,重新遍历剩余的活跃弹窗,逐个更新它们的top值,保持堆叠顺序。
修改后的完整代码
HTML
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <div id="wrapper"> <button type="button" onclick="showPopUp('test','body')">test</button> </div>
CSS
* { margin: 0; padding: 0; overflow: hidden; } #wrapper { height: 100vh; width: 100vw; position: relative; /* 确保弹窗基于wrapper定位 */ } /*popup part */ .toast:not(.showing):not(.show) { opacity: 1; } .toast { position: absolute; z-index: 10; right: 30px; border-radius: 12px; background: #fff; padding: 20px 35px 20px 25px; box-shadow: 0 6px 20px -5px rgba(0, 0, 0, 0.1); overflow: hidden; transform: translateX(calc(100% + 30px)); transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.35); margin-top: 10px; /* 弹窗之间的间距 */ } .toast.active { transform: translateX(0%); } .toast .toast-content { display: flex; align-items: center; } .toast-content .check { display: flex; align-items: center; justify-content: center; height: 35px; min-width: 35px; background-color: #4070f4; color: #fff; font-size: 20px; border-radius: 50%; } .toast-content .message { display: flex; flex-direction: column; margin: 0 20px; } .message .text { font-size: 16px; font-weight: 400; color: #666666; } .message .text.text-1 { font-weight: 600; color: #333; } .toast .close { position: absolute; top: 10px; right: 15px; padding: 5px; cursor: pointer; opacity: 0.7; } .toast .close:hover { opacity: 1; } .toast .progress { position: absolute; bottom: 0; left: 0; height: 3px; width: 100%; } .toast .progress:before { content: ""; position: absolute; bottom: 0; right: 0; height: 100%; width: 100%; background-color: #4070f4; } .progress.active:before { animation: progress 5s linear forwards; } @keyframes progress { 100% { right: 100%; } } button { padding: 12px 20px; font-size: 20px; outline: none; border: none; background-color: #4070f4; color: #fff; border-radius: 6px; cursor: pointer; transition: 0.3s; } button:hover { background-color: #0e4bf1; } .toast.active~button { pointer-events: none; }
JavaScript
const wrapper = document.getElementById('wrapper'); const POPUP_OFFSET = 90; // 弹窗高度+间距的总和,可根据实际样式调整 const INIT_TOP = 25; // 初始top值 function rearrangeToasts() { const activeToasts = document.querySelectorAll('.toast.active'); activeToasts.forEach((toast, index) => { toast.style.top = `${INIT_TOP + index * POPUP_OFFSET}px`; }); } function showPopUp(title, body) { const popup_data = ` <div class="toast active"> <div class="toast-content"> <i class="fas fa-solid fa-check check"></i> <div class="message"> <span class="text text-1">${title}</span> <span class="text text-2">${body}</span> </div> </div> <i class="fa-solid fa-xmark close"></i> <div class="progress active"></div> </div> `; wrapper.insertAdjacentHTML('beforeend', popup_data); const toast = wrapper.lastElementChild; const closeIcon = toast.querySelector(".close"); const progress = toast.querySelector(".progress"); // 计算并设置当前弹窗的top值 const activeCount = document.querySelectorAll('.toast.active').length; toast.style.top = `${INIT_TOP + (activeCount - 1) * POPUP_OFFSET}px`; let timer1 = setTimeout(() => { toast.classList.remove("active"); setTimeout(() => { toast.remove(); rearrangeToasts(); }, 500); // 等待动画结束后移除并重排 }, 5000); let timer2 = setTimeout(() => { progress.classList.remove("active"); }, 5300); closeIcon.addEventListener("click", () => { toast.classList.remove("active"); setTimeout(() => { progress.classList.remove("active"); toast.remove(); rearrangeToasts(); }, 300); clearTimeout(timer1); clearTimeout(timer2); }); }
内容的提问来源于stack exchange,提问作者rafaelHTML
相关产品推荐
相关产品推荐

