2023 年 5 月至 6 月发布的Chrome 114和 Edge 114配备了名为“Popover API”的 API。
弹出框和模态框之间的区别
弹出窗口和模态框都是显示在屏幕顶部的 UI,但关键的区别在于它们是否允许在显示时操作其他元素。显示模式时,无法进行滚动或单击其他元素等操作。另一方面,这次介绍的弹出窗口是一个 UI,允许您在显示时操作其他元素。
没有 JavaScript 的弹出窗口
使用 popover API,您可以在不使用 JavaScript 的情况下创建如下所示的 popover。
只需指定HTML属性即可实现
实现这个弹出窗口非常简单。
- popover将属性赋予弹出框主体并为其指定任意 id(id="popover1"等等)。
- 向要控制 popover 打开和关闭的按钮添加属性popovertarget,并指定 1 中给出的 id。
<!-- 控制弹出按钮 -->
<button popovertarget="popover1">Popover 1</button>
<!-- 弹窗 -->
<div id="popover1" popover class="popover-content">
<p>不使用JavaScript就可以实现最低限度的功能。</p>
</div>
<!-- 控制弹出按钮 -->
<button popovertarget="popover2">Popover 2</button>
<!-- 弹窗 -->
<div id="popover2" popover class="popover-content">
<p>打开第二个弹出按钮后,第一个自动关闭</p>
</div>
这就是创建一个简单的弹出窗口所需的全部内容。除了易于实现之外,popover API 还标配有有用的功能。
弹出框 API 中包含的标准功能
(1)放置在顶层
使用此 API 的弹出框主体默认放置在顶层。该层比较特殊,z-index总是出现在屏幕的顶部,无论其他元素的值如何。
(2) 可以轻松取消
您可以通过“在弹出窗口外部单击”或“按某个键”来Esc关闭弹出窗口。 MDN 文档称此为“轻微驳回”。
(3) 如果有多个popover,其他的会自动关闭
通常,屏幕上只能显示一个弹出窗口。因此,如果有两个弹出窗口,并且您在第一个弹出窗口打开时打开第二个弹出窗口,则第一个弹出窗口将自动关闭。
添加弹出窗口样式
- :popover-open:指向弹出窗口的打开状态的伪类。
- ::backdrop:放置在弹出框后面作为背景的伪元素。
/* :popover-open 开启时 */
.popover-content:popover-open {
width: 300px;
height: 120px;
border-radius: 8px;
border: none;
padding: 24px;
box-shadow: 8px 8px 10px #707070;
background: #ffffff;
}
/* 背景 ::backdrop */
.popover-content::backdrop {
background-color: #505050;
opacity: 0.5;
}
Toast 实现
这是一个实现 Toast 的示例,该 Toast 通知用户进程已完成或使用 popover API 出现错误。
手动显示/隐藏弹出窗口
在第一个例子中popover,popovertarget我们通过指定HTML属性来实现popover的显示/隐藏。
这次,我将使用JavaScript来手动切换显示/隐藏。
这里以下四点很重要。
- 指定创建为 toast 的div元素的popover属性。manual通过这样做,您可以创建一个不具有“轻松释放”和“屏幕上只有一个弹出窗口”功能的弹出窗口。
- 显示 toast 时showPopover()使用该方法。使用此方法显示时,它将被放置在顶层。
- hidePopover()使用一种方法来隐藏吐司。
- hidePopover()如果您这样做,该元素将保留在 DOM 中,因此remove()请使用该方法将其从 DOM 中删除。
const setupToast = ({ message, cssName }) => {
// 创建dom 添加至 body
const toast = createToastElm(message, cssName);
document.body.appendChild(toast);
// ⭐️showPopover 显示方法
toast.showPopover();
// -----省略-----
};
/**
* 创建Toast
* @param {string} message 文本内容
* @param {string} cssName cssName
* @return {HTMLDivElement} 返回dom
*/
const createToastElm = (message, cssName) => {
const toast = document.createElement("div");
// popover "manual" 指定属性
toast.popover = "manual";
// -----省略-----
// 关闭按钮
const closeButton = document.createElement("button");
closeButton.addEventListener("click", () => removeToast(toast));
toast.appendChild(closeButton);
return toast;
};
/**
* 删除 toast
* @param {HTMLDivElement} toast 删除dom
*/
const removeToast = (toast) => {
toast.hidePopover();
toast.remove();
};
切换事件检测
在此示例中,当创建或删除新 toast 时,toast 会重新排列并设置动画。处理顺序如下。
- 检测toast显示/隐藏
做动画
- 显示时(创建新的 toast): 底部轻柔地显示一个新的 toast。
- 隐藏时(toast 被删除):将整个内容向上移动
事件用于检测 toast 是显示还是隐藏toggle。
toggle该事件对于弹出框元素是唯一的,并且在显示或隐藏后立即触发。传递给此事件的event对象的值newState用于进一步分支动画。
// 显示和隐藏时重新排列
toast.addEventListener("toggle", (event) => {
alignToast(event.newState === "closed");
});
// -----省略-----
const alignToast = (withMoveAnim) => {
const toasts = document.querySelectorAll(".toast");
// 把Toast按顺序竖着摆
// withMoveAnim true:opacity translate 动画
// withMoveAnim false:opacity 动画
toasts.forEach((toast, index) => {
toast.style.transition = withMoveAnim
? "translate 0.2s linear, opacity 0.2s linear"
: "opacity 0.2s linear";
toast.style.translate = `0px ${(56 + 10) * index}px`;
toast.style.opacity = 1;
});
};
event.newState 当值为 closed 时,会执行动画将屏幕上剩余的 toast 移至顶部,因为 toast 已消失;否则(event.newState值为open)时,执行动画以显示它们。
除了动画之外,我们还实现了一个在显示3秒后自动删除toast的实现
反讽手法运用娴熟,令人会心一笑。
情感浓度过高可适当留白,以达平衡。
作者的布局谋篇匠心独运,让读者在阅读中享受到了思维的乐趣。
文章紧扣主题,观点鲜明,展现出深刻的思考维度。