CSS & JS Effect – Dialog Modal
效果

参考:
Youtube – Create a Simple Popup Modal
Youtube – Create a Modal (Popup) with HTML/CSS and JavaScript
重点
1. modal 就是一个 position: fixed 的大 overlay 黑影, 同时里面有居中的 content box.
2. modal 原本是 hide 起来的, 点击后 show 就可以了.
HTML
<body>
<button id="js-open-modal-btn" class="open-modal-btn">Open Modal</button>
<div id="js-modal" class="modal">
<div class="content">
<div class="header">
<h1>You are The Winner!</h1>
<button id="js-close-modal-btn" class="close-modal-btn"><i class="fa-solid fa-xmark"></i></button>
</div>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Repellat id, rem veniam ratione modi laboriosam consectetur vitae illum perspiciatis recusandae!</p>
</div>
</div>
</body>
modal 负责 backdrop 黑影
content 则是中间白色的内容区域
CSS Style

.open-modal-btn{
margin-top: 17rem;
margin-inline: auto;
display: block;
}
.modal {
visibility: hidden; // show / hide control
position: fixed; // 定位
inset: 0; // full viewport
background-color: rgb(0 0 0 / 0.2); // backdrop color
// 居中 content
display: flex;
justify-content: center;
align-items: center;
.content {
padding: 2rem 3rem;
max-width: 1024px;
background-color: white; // 因为 modal 是透明黑, 所以这里要 set 回白色
box-shadow: 0 2px 4px rgb(0 0 0 / 0.2); // 影子
.header{
display: flex;
justify-content: space-between;
align-items: center;
font-size: 3rem;
.close-modal-btn {
background-color: white;
font-size: inherit;
color: black;
border-width: 0;
cursor: pointer;
}
}
p{
margin-top: 2rem;
font-size: 2rem;
}
}
&.show {
visibility: initial; // clear visibility to show
}
}
看注释的地方解释就可以了, 其它是点缀而已
JavaScript
const modal = document.querySelector<HTMLElement>('#js-modal')!;
const openModalBtn = document.querySelector<HTMLButtonElement>('#js-open-modal-btn')!;
const closeModalBtn = document.querySelector<HTMLButtonElement>('#js-close-modal-btn')!;
openModalBtn.addEventListener('click', () => {
modal.classList.add('show');
});
closeModalBtn.addEventListener('click', () => {
modal.classList.remove('show');
});
只是简单的点击 add class 而已
Body Scroll IOS Safari Problem
modal 开启后, 通常体验不允许 body scroll. 一般的做法是给 body overflow: hidden
但是这个在 Safari 有 bug. 目前 status 是说已经 fixed 了, 但是我没用最新的 safari 测试, 所以不确定.
解决方法是让 body position fixed, 然后修改它的 top 到当前的 scroll top.
下面是我封装的方法
// note: 来龙去脉
// safari 没有办法通过 overflow hide 去阻止 body scroll
// 所以只能把 body 定位变成没有 scroll
// 这个 safari 问题已经很多年的了
// tesla, angular, stackoverflow 也是用这个方案去破
// bootstrap 倒没有处理这个, 比较 noob
// refer:
// https://bugs.webkit.org/show_bug.cgi?id=153852
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block const showedModals: string[] = []; export function toggleModal(modalName: string): void {
showedModals.includes(modalName) ? closeModal(modalName) : openModal(modalName);
} export function openModal(modalName: string): void {
if (showedModals.length === 0) {
const currentScrollTop = (document.scrollingElement ?? document.documentElement).scrollTop;
document.body.style.position = 'fixed';
document.body.style.width = '100%';
document.body.style.top = `-${currentScrollTop}px`;
}
showedModals.push(modalName);
} export function closeModal(modalName: string): void {
if (!showedModals.includes(modalName)) {
throw new Error(`Showed modals doesn't contains modal name: ${modalName}`);
}
if (showedModals.length === 1) {
const recoveryScrollTop = Math.abs(parseFloat(document.body.style.top));
document.body.style.removeProperty('position');
document.body.style.removeProperty('width');
document.body.style.removeProperty('top');
(document.scrollingElement ?? document.documentElement).scrollTop = recoveryScrollTop;
}
const indexOf = showedModals.indexOf(modalName);
showedModals.splice(indexOf, 1);
}
Mobile Back Button Close Dialog
大家习惯按手机的 back button 来试图关闭 Modal,
其实这是一个不正确的操作. 因为 back 会直接跳去上一页而不是关闭 Modal.
为了防止这样的错误体验. 我们可以做一点手脚.
step 1: 在打开 Modal的同时 history.push 放入 query param ?modal="show" (这样之后就可以 back 了)
step 2: 把关闭的操作 改成 history.back (统一关闭的方式)
step 3: 监听 onpopstate, 一旦发生就关闭 Modal.
step 4: 如果用户 refresh 那我们需要在 page load 的时候把 ?modal=show 用 history.replace 移除掉
以上的方法思路只是 for 简单场景的. 如果有多层 modal 或者更复杂的情况就 cover 不了了.
JS 代码:

const modal = document.querySelector<HTMLElement>('#js-modal')!;
const openModalBtn = document.querySelector<HTMLButtonElement>('#js-open-modal-btn')!;
const closeModalBtn = document.querySelector<HTMLButtonElement>('#js-close-modal-btn')!;
// 处理 page load
const { pathname, search, hash } = location;
const queryParams = new URLSearchParams(search);
if (queryParams.has('whatsAppDialog')) {
queryParams.delete('whatsAppDialog');
const newSearch = queryParams.toString() !== '' ? `?${queryParams.toString()}` : '';
history.replaceState(null, '', `${pathname}${newSearch}${hash}`);
}
openModalBtn.addEventListener('click', () => {
modal.classList.add('show');
// pushState
const { pathname, search, hash } = location;
const params = new URLSearchParams(search);
params.set('whatsAppDialog', 'open');
history.pushState(null, '', `${pathname}?${params.toString()}${hash}`);
// listen to 'back button'
window.addEventListener(
'popstate',
() => {
modal.classList.remove('show');
},
{ once: true }
);
});
closeModalBtn.addEventListener('click', () => {
history.back(); // back
});
另外, 未来或许可以用 Navigation API 实现, 可能会更方便. 以后才研究. 毕竟现在许多 browser 还不支持.

其它没有提到的
1. backdrop click, Keyboard Esc 关闭.
2. show 的 animation, backdrop fadein, content scale in
3. z-index 问题. 要确保 z-index 够高最好是把 modal 放到 body 最下方 (Angular Material 就这么做的)
但放出去后要注意 CSS Style 哦. element 结构在外面了就不可以 depend on ancestor 了.
CSS & JS Effect – Dialog Modal的更多相关文章
- [转]jQuery UI Dialog Modal Popup Yes No Confirm example in ASP.Net
本文转自:http://www.aspsnippets.com/Articles/jQuery-UI-Dialog-Modal-Popup-Yes-No-Confirm-example-in-ASPN ...
- HTML/CSS/JS编码规范
最近整理了一份HTML/CSS/JS编码规范,供大家参考.目录:一.HTML编码规范二.CSS编码规范三.JS编码规范 一.HTML编码规范 1. img标签要写alt属性 根据W3C标准,img标签 ...
- CSS & JS 制作滚动幻灯片
==================纯CSS方式==================== <!DOCTYPE html> <html> <head> <met ...
- 【转】Maven Jetty 插件的问题(css/js等目录死锁)的解决
Maven Jetty 插件的问题(css/js等目录死锁,不能自动刷新)的解决: 1. 打开下面的目录:C:\Users\用户名\.m2\repository\org\eclipse\jetty ...
- Css Js Loader For Zencart
Css Js Loader 描述:这个插件很早就出来了,可能知道人非常少 这个插件的功能是整合所有的网站的CSS和JS内容到一个文件里边. 因为CSS和JS文件到了一个文件,加快了程序的运行 在配合其 ...
- 购物车数字加减按钮HTML+CSS+JS(有需要嫌麻烦的小伙伴拿走不谢)
之前在写详情页的时候,如下图 因为自己嫌麻烦,就去看其他网站是怎么写的,想直接拿来用,后来看来看去觉得写得很麻烦,于是最后还是决定自己写,附上HTML+CSS+JS代码,一条龙一站式贴心服务2333 ...
- vs合并压缩css,js插件——Bundler & Minifier
之前做了一个大转盘的抽奖活动,因为比较火,部分用户反馈看不到页面的情况,我怀疑js加载请求过慢导致,所以今天针对之前的一个页面进行调试优化. 首先想到的是对页面的js和css进行压缩优化,百度了下vs ...
- nginx资源定向 css js路径问题
今天玩玩项目,学学nginx发现还不错,速度还可以,但是CSS JS确无法使用,原来Iginx配置时需要对不同类型的文件配置规则,真是很郁闷,不过想想也还是很有道理.闲暇之际,把配置贴上来.#user ...
- IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求
今天将开发好的ASP.NET站点部署到客户的服务器上后,发现了一个非常头疼的问题,那么就是IIS7的应用程序池是集成模式的话,ASP.NET项目中自定义的HttpModule会处理静态文件(.html ...
- 网站加载css/js/img等静态文件失败
网站加载css/js/img等静态文件失败,报网站http服务器内部500错误.而服务器中静态文件存在且权限正常. 从浏览器中直接访问文件,出来乱码.这种问题原因在于iis中该网站mime配置报错,不 ...
随机推荐
- 部分解决 | ocrmypdf对中文pdf进行ocr识别后存在多余空格
1.问题 ocrmypdf安装采用的是在windows安装方法具体看 https://media.readthedocs.org/pdf/ocrmypdf/latest/ocrmypdf.pdf 由于 ...
- Mysql-explain之Using temporary和Using filesort解决方案
第一条语句 explainselect * from tb_wm_shop where is_delete != 1 and is_authentication = 1 ORDER BY create ...
- BS架构和CS架构应用
概述 B/S结构即浏览器和服务器结构.它是随着Internet技术的兴起,对C/S结构的一种变化或者改进的结构.在这种结构下,用户工作界面是通过WWW浏览器来实现,极少部分事务逻辑在前端(Br ...
- Python 正则表达式实战之Java日志解析
需求描述 基于生产监控告警需求,需要对Java日志进行解析,提取相关信息,作为告警通知消息的内容部分. 提取思路 具体怎么提取,提取哪些内容呢?这里笔者分析了大量不同形态的生产日志,最后总结出4种形态 ...
- PHP 高性能框架 Workerman 凭什么能硬刚 Swoole ?
大家好,我是码农先森. 一次偶然看到了国外某机构针对 PHP 周边生态框架及扩展的性能测试排行榜,看到 Workerman 竟遥遥领先 Swoole.在我们 PHP 程序员现有的认知里,Swoole ...
- Fiddler篡改请求和响应数据
Fiddler标记断点后,我们可以通过篡改请求或响应数据,来模拟客户端请求和服务器响应. 一.打断点的方式 1.1 工具栏设置断点 工具栏勾选断点类型进行断点,路径:Rules->Automat ...
- python画图报错:OSError: 'seaborn-whitegrid' is not a valid package style
解决方法: https://stackoverflow.com/questions/78019854/matplotlib-seaborn-whitegrid-is-not-a-valid-packa ...
- "观察者" 网站上不错的对社会问题进行思考的文章--------------- 分享
原文地址: https://www.guancha.cn/weichengling 在网上无意中到了下面的文章,对社会热点问题有较为全面的思考,感觉不错,分享一下. ------------ ...
- 【转载】 AI助力神经科学:DeepMind 复现大脑空间导航方式
原文地址: https://baijiahao.baidu.com/s?id=1600279012514462353&wfr=spider&for=pc =============== ...
- 关于mysql配置文件中jdbc url 的记录
版本不同 url不同 大同小异 基本就是不同参数配置的区别 maven 仓库地址 https://mvnrepository.com/artifact/mysql/mysql-connector-ja ...