如何使用 CSS 和 JavaScript 创建动态弹出窗口
介绍
弹出框是一种常见的 UI 模式,用于显示上下文信息或提供快速访问操作的功能。它们通常用于仪表板、数据表或表单。在本教程中,我将向您展示如何使用 **CSS** 和 **JavaScript** 创建 **完全动态的弹出框**。
该解决方案旨在应对以下挑战:
在本教程结束时,您将拥有一个可重复使用且响应迅速的弹出窗口实现。
步骤 1:HTML 结构
首先,我们假设一个基本的 HTML 结构,单击 **SVG** 图标即可触发弹出窗口。以下是列标题组的示例,您可能在数据表中找到此类标题组:
Example Title``` The **popover** will be dynamically created and injected into the DOM when the user clicks on the `
弹出箭头样式
箭头是沿弹出窗口边缘放置的旋转小方块。默认情况下,箭头指向下方。
.popover:after { content: ""; background-color: #fff; /* Matches the popover's background */ border-color: #E2E8F0; /* Matches the popover's border */ border-right-width: 1px; border-bottom-width: 1px; height: 1rem; width: 1rem; position: absolute; bottom: -0.5rem; /* Positioned below the popover */ left: 50%; /* Centered horizontally */ transform: translateX(-50%) rotate(45deg); /* Creates the arrow shape */ }
箭头位置调整
根据弹出窗口的位置,箭头的位置需要动态变化。以下类可调整箭头的位置:
**顶部箭头**
当弹出窗口位于触发元素下方时,箭头指向上方。
.popover.arrow-top:after { bottom: auto; /* Remove the default bottom positioning */ top: -0.5rem; /* Position the arrow above the popover */ border-bottom-width: 0; /* Hide the bottom border */ border-top-width: 1px; /* Show the top border */ }
**右侧箭头**
当弹出窗口与触发元素左侧对齐时,箭头指向右侧。
.popover.arrow-right:after { top: 50%; /* Vertically centered */ left: 97%; /* Positioned to the right of the popover */ bottom: auto; /* Remove the default bottom positioning */ transform: translateY(-50%) rotate(45deg); /* Keeps the arrow correctly aligned */ border-right-width: 1px; /* Show the right border */ border-left-width: 0; /* Hide the left border */ }
动态箭头定位
使用这些样式,箭头可以根据**弹出框的位置**动态调整其位置。这为与您的 UI 交互的用户创造了无缝且响应迅速的体验。
步骤 3:添加 JavaScript 以实现动态行为
为了使弹出窗口具有交互性和动态定位性,我们将使用 JavaScript。此脚本负责在用户与页面交互时创建、定位和清理弹出窗口。
初始设置
脚本首先选择所有将充当弹出窗口触发器的 SVG 图标。我们还维护一个状态变量“isPopoverOpen”,以跟踪弹出窗口当前是否显示。
document.querySelectorAll('.dt-column-title-group svg').forEach((svgIcon) => { let isPopoverOpen = false; svgIcon.addEventListener('click', (event) => { event.stopPropagation(); // Prevent event propagation to avoid unwanted behavior let popoverContainer = document.querySelector('.popover-container'); if (isPopoverOpen) { // If the popover is open, remove it popoverContainer?.remove(); isPopoverOpen = false; return; } if (!popoverContainer) { const popoverElement = document.createElement('div'); popoverElement.innerHTML = 'Popover Content'; popoverElement.classList.add('popover-container'); document.body.appendChild(popoverElement); const popoverHTML = popoverElement.querySelector('.popover');
弹出框定位逻辑
`positionPopover` 函数根据触发器的位置(`svgIcon`)计算弹出窗口的理想位置,并确保其适合视口。箭头的位置也会动态调整。
const positionPopover = () => { const targetRect = svgIcon.getBoundingClientRect(); let topPosition = targetRect.top - popoverElement.offsetHeight - 20; // Default: above the element let leftPosition = targetRect.left + targetRect.width / 2 - popoverElement.offsetWidth / 2; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // Reset arrow classes popoverHTML?.classList.remove('arrow-top', 'arrow-bottom', 'arrow-left', 'arrow-right'); // Adjust if the popover exceeds the top boundary if (topPosition < 0) { topPosition = targetRect.bottom + 10; // Place below the element popoverHTML?.classList.add('arrow-top'); // Arrow on top } else { popoverHTML?.classList.add('arrow-bottom'); // Arrow on bottom } // Adjust if the popover exceeds the bottom boundary if (topPosition + popoverElement.offsetHeight > viewportHeight) { topPosition = targetRect.top - popoverElement.offsetHeight - 10; // Place above the element popoverHTML?.classList.remove('arrow-bottom'); popoverHTML?.classList.add('arrow-top'); // Arrow on top } // Adjust if the popover exceeds the right boundary if (leftPosition + popoverElement.offsetWidth > viewportWidth) { leftPosition = targetRect.left - popoverElement.offsetWidth - 20; // Place to the left topPosition = targetRect.top; // Align with the top of the trigger popoverHTML?.classList.remove('arrow-bottom', 'arrow-top'); popoverHTML?.classList.add('arrow-right'); // Arrow on the right } // Adjust if the popover exceeds the left boundary if (leftPosition < 0) { leftPosition = targetRect.right + 20; // Place to the right topPosition = targetRect.top; // Align with the top of the trigger popoverHTML?.classList.remove('arrow-bottom', 'arrow-top'); popoverHTML?.classList.add('arrow-left'); // Arrow on the left } popoverElement.style.position = 'fixed'; popoverElement.style.top = `${topPosition}px`; popoverElement.style.left = `${leftPosition}px`; }; positionPopover(); // Initial positioning
处理滚动事件
为了让弹出窗口在用户滚动时保持正确位置,我们添加了一个“scroll”事件监听器:
const handleScroll = () => { if (isPopoverOpen) { positionPopover(); } }; window.addEventListener('scroll', handleScroll);
清理:关闭弹出窗口
当用户点击弹出窗口外部或触发器不再可见时,应移除弹出窗口。这通过两个事件监听器进行管理:
document.addEventListener('click', function handleClickOutside(event) { if (!popoverElement.contains(event.target) && !svgIcon.contains(event.target)) { popoverElement.remove(); isPopoverOpen = false; document.removeEventListener('click', handleClickOutside); window.removeEventListener('scroll', handleScroll); } });
const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (!entry.isIntersecting) { popoverElement.remove(); isPopoverOpen = false; observer.disconnect(); window.removeEventListener('scroll', handleScroll); } }); }, { threshold: 0.1 }); observer.observe(svgIcon);
步骤 4:测试 Popover
在为弹出窗口实现 **HTML**、**CSS** 和 **JavaScript** 后,测试其在不同场景下的行为非常重要。请按照以下步骤确保您的弹出窗口按预期工作。
1. 基本功能
2.动态定位
3.响应能力
4. 边缘情况
5. 最后的润色
结论
测试弹出窗口可确保在不同场景中实现无缝的用户体验。通过解决极端情况并确保响应能力,您的弹出窗口实现将非常可靠且可用于生产环境。
让我知道这对您的项目有何作用,并随时分享您的见解或定制!