当前位置: 首页 > news >正文

JavaScript 对话框式弹出提示框 PopoverTip 实现详解

在网页开发中,我们经常需要使用弹出框来展示额外的信息或操作选项。不同于模态对话框,弹出提示框(Popover)通常依附于触发元素,以更轻量的方式呈现内容。本文将详细解析一个简单的 JavaScript 对话框式弹出提示框的实现过程,并附带代码分析和最终效果展示。

HTML 结构

<div class="popover-wrapper"><button class="popover-trigger" data-placement="auto">自动弹出</button><div class="popover-tip"><div style="width: 100px; height: auto;">这是弹出框的内容,可以根据需要调整大小。</div></div>
</div>
  • .popover-wrapper:作为外部容器包裹触发元素和弹出框,利用相对定位方便弹出框定位。
  • .popover-trigger:触发弹出框的按钮,可以是任何元素。data-placement 属性控制弹出方向,支持 topbottomleftright 和 auto(自动判断)。
  • .popover-tip:弹出框容器,初始状态下隐藏,内部的 div 元素用于设置弹出框的尺寸和内容。

CSS 样式

.popover-tip {display: none; /* 默认隐藏 */position: absolute; /* 绝对定位 */visibility: hidden; /* 隐藏但不影响布局,用于获取尺寸 */background-color: #fff;border: 1px solid #ccc;padding: 10px;z-index: 10; /* 确保弹出框在上方 */
}.popover-tip.show {display: block;visibility: visible;
}.popover-tip::before { /* 使用伪元素创建三角形箭头 */content: '';position: absolute;border: 6px solid transparent;z-index: -1; /* 确保箭头在弹出框下方 */
}.popover-tip.top::before {border-top-color: inherit; /* 箭头颜色继承弹出框边框颜色 */left: 50%;top: 100%;transform: translateX(-50%); /* 水平居中 */
}
/* 其他方向箭头样式类似,调整边框颜色和位置 */
  • .popover-tip:默认隐藏,使用绝对定位。设置背景色、边框、内边距等样式。visibility: hidden 确保在计算尺寸时不影响布局。
  • .popover-tip.show:显示弹出框,同时设置可见性。
  • .popover-tip::before:利用伪元素创建三角形箭头,根据不同弹出方向设置边框颜色和位置,实现箭头指向效果。

JavaScript 实现

const triggers = document.querySelectorAll('.popover-trigger');triggers.forEach(trigger => {trigger.addEventListener('click', function(event) {const popover = this.nextElementSibling || this.parentElement.querySelector('.popover-tip');let placement = this.dataset.placement || 'top';// 计算弹出框位置const triggerRect = this.getBoundingClientRect();const windowHeight = window.innerHeight;const windowWidth = window.innerWidth;// 先将弹出框显示出来,以便获取其尺寸popover.style.display = 'block';const popoverRect = popover.getBoundingClientRect();// 计算初始位置(相对于按钮)let top = triggerRect.top + window.scrollY;let left = triggerRect.left + window.scrollX;// 根据位置和弹出框尺寸调整if (placement === 'auto') {// ... 自动判断最佳弹出方向的逻辑 ...} else {switch (placement) {case 'top':top -= popoverRect.height + 8;left += (triggerRect.width - popoverRect.width) / 2;break;// ... 其他方向的定位逻辑 ...}}// 应用位置和显示弹出框popover.style.top = `${top}px`;popover.style.left = `${left}px`;popover.classList.add('show', placement);// 点击页面其他地方关闭弹出框document.addEventListener('click', function closePopover(event) {if (!trigger.contains(event.target) && !popover.contains(event.target)) {popover.classList.remove('show', 'top', 'bottom', 'left', 'right');document.removeEventListener('click', closePopover);}});event.stopPropagation(); // 阻止事件冒泡});
});
  1. 获取所有触发元素和绑定事件:使用 querySelectorAll 获取所有 class 为 .popover-trigger 的元素,并为每个元素绑定点击事件监听器。
  2. 获取弹出框元素和弹出方向:获取与触发元素关联的 .popover-tip元素,以及 data-placement 属性指定的弹出方向。
  3. 计算弹出框位置
    • 先将弹出框临时设置为 display: block,获取其尺寸信息。
    • 根据弹出方向和触发元素的位置计算弹出框的 top 和 left 值,确保弹出框相对于触发元素正确显示。
    • 自动弹出方向 (auto) 需要判断上下左右的可用空间,选择最佳的弹出方向。
  4. 应用位置和显示弹出框:设置弹出框的 top 和 left 样式,并添加 .show 类,使其显示出来。
  5. 点击空白处关闭弹出框:为 document 绑定点击事件,判断点击位置是否在触发元素或弹出框内部,如果不是则关闭弹出框。
  6. 阻止事件冒泡:使用 event.stopPropagation() 阻止事件冒泡,避免点击弹出框区域时触发 document 的点击事件,导致弹出框立即关闭。

效果展示

完整html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>对话框样式的弹出提示框 PopoverTip </title><style>.popover-wrapper {position: relative;display: inline-block;}.popover-tip {display: none;position: absolute;visibility: hidden; /* 隐藏,但会渲染 */background-color: #fff;border: 1px solid #ccc;border-radius: 5px;padding: 10px;z-index: 10;}.popover-tip.show {display: block;visibility: visible;}.popover-tip::before {content: '';position: absolute;border: 6px solid transparent;z-index: -1;}/* 箭头样式 */.popover-tip.top::before {border-top-color: inherit;left: 50%;top: 100%;transform: translateX(-50%);}.popover-tip.bottom::before {border-bottom-color: inherit;left: 50%;bottom: 100%;transform: translateX(-50%);}.popover-tip.left::before {border-left-color: inherit;top: 50%;left: 100%;transform: translateY(-50%);}.popover-tip.right::before {border-right-color: inherit;top: 50%;right: 100%;transform: translateY(-50%);}</style>
</head>
<body style="height: 100vh;display: flex;justify-content: center;align-items: center;"><div class="popover-wrapper"><button class="popover-trigger" data-placement="auto">自动弹出</button><div class="popover-tip"><div style="width: 100px; height: auto;">这是弹出框的内容,可以根据需要调整大小。</div></div></div><div class="popover-wrapper"><button class="popover-trigger" data-placement="right">另一个弹出</button><div class="popover-tip"><div style="width: 100px; height: auto;">这是弹出框的内容,可以根据需要调整大小。</div></div></div><script>const triggers = document.querySelectorAll('.popover-trigger');triggers.forEach(trigger => {trigger.addEventListener('click', function(event) {const popover = this.nextElementSibling || this.parentElement.querySelector('.popover-tip');let placement = this.dataset.placement || 'top';// 计算弹出框位置const triggerRect = this.getBoundingClientRect();const windowHeight = window.innerHeight;const windowWidth = window.innerWidth;// 先将弹出框显示出来,以便获取其尺寸popover.style.display = 'block';const popoverRect = popover.getBoundingClientRect();let top = 0;//triggerRect.top;let left = 0;//triggerRect.left;// 自动调整位置if (placement === 'auto') {const spaceAbove = triggerRect.top;const spaceBelow = windowHeight - triggerRect.bottom;const spaceLeft = triggerRect.left;const spaceRight = windowWidth - triggerRect.right;if (spaceAbove >= popoverRect.height && spaceAbove >= spaceBelow) {placement = 'top';top -= popoverRect.height + 8; // 8px 箭头偏移// 水平居中left += (triggerRect.width - popoverRect.width) / 2;} else if (spaceBelow >= popoverRect.height) {placement = 'bottom';top += triggerRect.height + 8;// 水平居中left += (triggerRect.width - popoverRect.width) / 2;} else if (spaceLeft >= popoverRect.width && spaceLeft >= spaceRight) {placement = 'left';left -= popoverRect.width + 8;// 垂直居中top += (triggerRect.height - popoverRect.height) / 2;} else if (spaceRight >= popoverRect.width) {placement = 'right';left += triggerRect.width + 8;// 垂直居中top += (triggerRect.height - popoverRect.height) / 2;} else {// 如果空间不足,默认顶部弹出placement = 'top';top -= popoverRect.height + 8; // 8px 箭头偏移// 水平居中left += (triggerRect.width - popoverRect.width) / 2;}} else {// 非自动模式下,根据选择的位置调整switch (placement) {case 'top':top -= popoverRect.height + 8;// 水平居中left += (triggerRect.width - popoverRect.width) / 2;break;case 'bottom':top += triggerRect.height + 8;// 水平居中left += (triggerRect.width - popoverRect.width) / 2;break;case 'left':left -= popoverRect.width + 8;// 垂直居中top += (triggerRect.height - popoverRect.height) / 2;break;case 'right':left += triggerRect.width + 8;// 垂直居中top += (triggerRect.height - popoverRect.height) / 2;break;}}// 应用位置和显示弹出框popover.style.top = `${top}px`;popover.style.left = `${left}px`;popover.classList.add('show', placement);// 点击页面其他地方关闭弹出框document.addEventListener('click', function closePopover(event) {if (!trigger.contains(event.target) && !popover.contains(event.target)) {popover.classList.remove('show', 'top', 'bottom', 'left', 'right');document.removeEventListener('click', closePopover);}});event.stopPropagation();});});</script>
</body>
</html>

最终效果是一个功能完善的弹出提示框组件,可以根据触发元素的位置自动调整弹出方向,并支持点击空白处关闭。

总结

本文深入解析了如何使用 HTML、CSS 和 JavaScript 实现一个灵活且易于使用的弹出提示框组件。该组件结构清晰,代码易懂,并具备自动调整弹出方向、点击空白处关闭等实用功能,可以作为学习 JavaScript 交互效果开发的良好案例。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JavaFX布局-ToolBar
  • 【前端】JavaScript入门及实战111-115
  • 关于C/C++的编译、构建、CMake、x86_amd64等问题(自用)
  • 浅谈Trie树算法
  • 开启休假模式
  • WebSocket 协议与 HTTP 协议、定时轮询技术、长轮询技术
  • Linux 安装 Redis 6.2.14
  • vulhub靶场之wordpress关卡(保姆级教程)
  • 大数据Flink(一百零七):阿里云Flink的应用场景
  • npm ERR! missing script: serve
  • 基于MPC在线优化的有效集法位置控制器simulink建模与仿真
  • 免杀笔记 ---> 函数踩踏 PEB寻址
  • 获取UTC时间计算时间
  • POE服务机器人-快速开始
  • <Rust>使用rust实现crc16_modbus校验码生成?
  • 深入了解以太坊
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • Facebook AccountKit 接入的坑点
  • Java编程基础24——递归练习
  • Making An Indicator With Pure CSS
  • mongodb--安装和初步使用教程
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • orm2 中文文档 3.1 模型属性
  • underscore源码剖析之整体架构
  • 从零搭建Koa2 Server
  • 从零开始的无人驾驶 1
  • 机器学习中为什么要做归一化normalization
  • 少走弯路,给Java 1~5 年程序员的建议
  • 数据仓库的几种建模方法
  • 听说你叫Java(二)–Servlet请求
  • 线性表及其算法(java实现)
  • 小试R空间处理新库sf
  • 数据可视化之下发图实践
  • #70结构体案例1(导师,学生,成绩)
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (笔试题)合法字符串
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET Core WebAPI中封装Swagger配置
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .NET IoC 容器(三)Autofac
  • .NET Micro Framework初体验(二)
  • .net mvc 获取url中controller和action
  • .net/c# memcached 获取所有缓存键(keys)
  • .net6+aspose.words导出word并转pdf
  • .NET框架设计—常被忽视的C#设计技巧
  • .Net下的签名与混淆
  • .NET性能优化(文摘)
  • .vue文件怎么使用_vue调试工具vue-devtools的安装