效果

参考:

Youtube – Create a Simple Popup Modal

Youtube – Create a Modal (Popup) with HTML/CSS and JavaScript

HTML – Native Dialog Modal

Boostrap Modal

Angular Material Dialog

重点

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的更多相关文章

  1. [转]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 ...

  2. HTML/CSS/JS编码规范

    最近整理了一份HTML/CSS/JS编码规范,供大家参考.目录:一.HTML编码规范二.CSS编码规范三.JS编码规范 一.HTML编码规范 1. img标签要写alt属性 根据W3C标准,img标签 ...

  3. CSS & JS 制作滚动幻灯片

    ==================纯CSS方式==================== <!DOCTYPE html> <html> <head> <met ...

  4. 【转】Maven Jetty 插件的问题(css/js等目录死锁)的解决

    Maven Jetty 插件的问题(css/js等目录死锁,不能自动刷新)的解决:   1. 打开下面的目录:C:\Users\用户名\.m2\repository\org\eclipse\jetty ...

  5. Css Js Loader For Zencart

    Css Js Loader 描述:这个插件很早就出来了,可能知道人非常少 这个插件的功能是整合所有的网站的CSS和JS内容到一个文件里边. 因为CSS和JS文件到了一个文件,加快了程序的运行 在配合其 ...

  6. 购物车数字加减按钮HTML+CSS+JS(有需要嫌麻烦的小伙伴拿走不谢)

    之前在写详情页的时候,如下图 因为自己嫌麻烦,就去看其他网站是怎么写的,想直接拿来用,后来看来看去觉得写得很麻烦,于是最后还是决定自己写,附上HTML+CSS+JS代码,一条龙一站式贴心服务2333 ...

  7. vs合并压缩css,js插件——Bundler & Minifier

    之前做了一个大转盘的抽奖活动,因为比较火,部分用户反馈看不到页面的情况,我怀疑js加载请求过慢导致,所以今天针对之前的一个页面进行调试优化. 首先想到的是对页面的js和css进行压缩优化,百度了下vs ...

  8. nginx资源定向 css js路径问题

    今天玩玩项目,学学nginx发现还不错,速度还可以,但是CSS JS确无法使用,原来Iginx配置时需要对不同类型的文件配置规则,真是很郁闷,不过想想也还是很有道理.闲暇之际,把配置贴上来.#user ...

  9. IIS7的集成模式下如何让自定义的HttpModule不处理静态文件(.html .css .js .jpeg等)请求

    今天将开发好的ASP.NET站点部署到客户的服务器上后,发现了一个非常头疼的问题,那么就是IIS7的应用程序池是集成模式的话,ASP.NET项目中自定义的HttpModule会处理静态文件(.html ...

  10. 网站加载css/js/img等静态文件失败

    网站加载css/js/img等静态文件失败,报网站http服务器内部500错误.而服务器中静态文件存在且权限正常. 从浏览器中直接访问文件,出来乱码.这种问题原因在于iis中该网站mime配置报错,不 ...

随机推荐

  1. python tkinter | 如何使得entry文本框靠右显示,从右向左填充,显示文本末尾

    from tkinter import * from tkinter import filedialog app = Tk() app.title("ABC") app.geome ...

  2. iOS开发基础102-后台保活方案

    iOS系统在后台执行程序时,有严格的限制,为了更好地管理资源和电池寿命,iOS会限制应用程序在后台的运行时间.然而,iOS提供了一些特定的策略和技术,使得应用程序可以在特定场景下保持后台运行(即&qu ...

  3. jQuery中hide()和display的区别在于它们实现元素隐藏的方式不同。

    1. hide()方法是jQuery提供的一个函数,用于隐藏元素.当使用hide()方法时,元素会被设置为display:none,即不显示在页面上,但仍然占据着原来的空间.隐藏后的元素可以通过调用s ...

  4. leetcode简单(数组,字符串,链表):[66, 67, 70, 83, 121, 141, 160, 169, ,206, 338]

    目录 66. 加一 67. 二进制求和 70. 爬楼梯 83. 删除排序链表中的重复元素 121. 买卖股票的最佳时机 141. 环形链表 160. 相交链表 169. 多数元素 206. 反转链表 ...

  5. windows下mysql服务局域网访问配置

    在局域网中访问本机(Windows)的MySQL服务器,需要确保MySQL服务器配置为允许远程访问,并且防火墙设置允许外部连接.以下是详细的步骤: 1. 修改MySQL配置文件允许远程访问 找到并编辑 ...

  6. 常用ffmpeg命令集合(收藏吧,帮你省一个录屏软件的会员费)

    录制屏幕: 全屏的话用screen-capture-recoder下载地址:https://github.com/rdp/screen-capture-recorder-to-video-window ...

  7. 查询当前网段的所有在用IP

    查询当前网段的所有在用IP For /L %i in (0,1,254) DO ping 192.168.10.%i >>D:\IP.txt https://www.cnblogs.com ...

  8. 【SQL】 牛客网SQL训练Part3 较难难度

    获取当前薪水第二多的员工的emp_no以及其对应的薪水salary 请你查找薪水排名第二多的员工编号emp_no.薪水salary.last_name以及first_name,不能使用order by ...

  9. 【VMware】将NAT虚拟机开放访问

    NAT模式下面需要将主机内的虚拟机提供给外部访问 这个设置可以通过开启端口来实现外部访问NAT虚拟机 主机端口 - 映射 虚拟机 IP 的端口,问题是有多少个虚拟机应用就需要开多少个端口...

  10. 【Linux】Re03

    一.软连接 语法用法 ln -s 源文件或者目录位置 链接名称 [root@localhost ~]# mkdir -p aa/bb/cc/dd [root@localhost ~]# ln -s a ...