Popover API
Popover API: 内置的弹框能力
popover
具有
auto
状态的弹窗可以通过在弹窗之外的区域进行选择,以达到“轻触关闭”的目的,并且通常一次仅允许屏幕上显示一个弹窗
manual
弹窗必须始终明确隐藏,但可以用于菜单中嵌套弹窗等使用情况。
<button type="button" popovertarget="popover">打开弹窗 with popovertarget</button>
<div id="popover" class="popover" popover="auto">
<div>弹窗内容</div>
</div>
popover="auto"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popover API</title>
<style>
.popover {
--popover-status: none;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
&::backdrop {
background-color: rgba(0, 0, 0, 0.3);
}
&:popover-open {
/* 弹窗显示动画 */
animation: popover-in 0.24s ease;
}
&:not(:popover-open):not(dialog[open]) {
display: var(--popover-status);
/* 弹窗离开动画 */
animation: popover-out 0.24s ease;
}
}
@keyframes popover-in {
from {
opacity: 0;
translate: 0 -100%;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes popover-out {
from {
opacity: 1;
translate: 0 0;
}
to {
opacity: 0;
translate: 0 -100%;
}
}
</style>
</head>
<body>
<strong>Popover API</strong>
<hr />
<button type="button" popovertarget="popover">打开弹窗 with popovertarget</button>
<div id="popover" class="popover" popover="auto">
<div>弹窗内容</div>
</div>
<script>
// Popover API: 内置的弹框能力
// 具有 auto 状态的弹窗可以通过在弹窗之外的区域进行选择,以达到“轻触关闭”的目的,并且通常一次仅允许屏幕上显示一个弹窗
// manual 弹窗必须始终明确隐藏,但可以用于菜单中嵌套弹窗等使用情况。
const popover = document.querySelector('.popover');
popover.addEventListener('beforetoggle', (e) => {
console.log('before toggle', e);
// 阻止默认行为, 阻止弹窗显示
// e.preventDefault();
});
popover.addEventListener('toggle', async (e) => {
console.log('toggle', e);
});
const popoverAnimate = {
'popover-in'() {
popover.style.setProperty('--popover-status', 'unset');
},
'popover-out'() {
popover.style.setProperty('--popover-status', 'none');
},
};
popover.addEventListener('animationend', (e) => {
popoverAnimate[e.animationName]?.();
});
</script>
</body>
</html>
popover="manual"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popover API</title>
<style>
.popover {
--popover-status: none;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
&::backdrop {
background-color: rgba(0, 0, 0, 0.3);
}
&:popover-open {
/* 弹窗显示动画 */
animation: popover-in 0.24s ease;
}
&:not(:popover-open):not(dialog[open]) {
display: var(--popover-status);
/* 弹窗离开动画 */
animation: popover-out 0.24s ease;
}
}
@keyframes popover-in {
from {
opacity: 0;
translate: 0 -100%;
}
to {
opacity: 1;
translate: 0 0;
}
}
@keyframes popover-out {
from {
opacity: 1;
translate: 0 0;
}
to {
opacity: 0;
translate: 0 -100%;
}
}
</style>
</head>
<body>
<strong>Popover API</strong>
<hr />
<button type="button" id="btn-open">打开弹窗</button>
<div class="popover" popover="manual">
<div>弹窗内容2</div>
<button type="button" class="btn-close">关闭弹窗</button>
</div>
<script>
// Popover API: 内置的弹框能力
// 具有 auto 状态的弹窗可以通过在弹窗之外的区域进行选择,以达到“轻触关闭”的目的,并且通常一次仅允许屏幕上显示一个弹窗
// manual 弹窗必须始终明确隐藏,但可以用于菜单中嵌套弹窗等使用情况。
const popover = document.querySelector('.popover');
const openBtn = document.getElementById('btn-open');
const closeBtn = document.querySelector('.btn-close');
openBtn.addEventListener('click', () => {
// 显示弹窗
popover.showPopover();
});
closeBtn.addEventListener('click', () => {
// 关闭弹窗
popover.hidePopover();
});
popover.addEventListener('beforetoggle', (e) => {
console.log('before toggle', e);
// 阻止默认行为, 阻止弹窗显示
// e.preventDefault();
});
popover.addEventListener('toggle', async (e) => {
console.log('toggle', e);
});
const popoverAnimate = {
'popover-in'() {
popover.style.setProperty('--popover-status', 'unset');
},
'popover-out'() {
popover.style.setProperty('--popover-status', 'none');
},
};
popover.addEventListener('animationend', (e) => {
popoverAnimate[e.animationName]?.();
});
</script>
</body>
</html>
Element.animate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popover API</title>
<style>
.popover {
--popover-status: none;
padding: 20px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
&::backdrop {
background-color: rgba(0, 0, 0, 0.3);
}
&:not(:popover-open):not(dialog[open]) {
display: var(--popover-status);
}
}
</style>
</head>
<body>
<strong>Popover API</strong>
<hr />
<button type="button" id="btn-open">打开弹窗</button>
<div class="popover" popover="manual">
<div>弹窗内容2</div>
<button type="button" class="btn-close">关闭弹窗</button>
</div>
<script>
// Popover API: 内置的弹框能力
// 具有 auto 状态的弹窗可以通过在弹窗之外的区域进行选择,以达到“轻触关闭”的目的,并且通常一次仅允许屏幕上显示一个弹窗
// manual 弹窗必须始终明确隐藏,但可以用于菜单中嵌套弹窗等使用情况。
const popover = document.querySelector('.popover');
const openBtn = document.getElementById('btn-open');
const closeBtn = document.querySelector('.btn-close');
openBtn.addEventListener('click', () => {
// 显示弹窗
popover.showPopover();
});
closeBtn.addEventListener('click', () => {
// 关闭弹窗
popover.hidePopover();
});
/** @type {Animation} */
let animate;
popover.addEventListener('beforetoggle', (e) => {
console.log('before toggle', e);
// 阻止默认行为, 阻止弹窗显示
// e.preventDefault();
if (e.newState === 'open') {
animate = popover.animate(
[
{
opacity: 0,
translate: '0 -100%',
},
{
opacity: 1,
translate: '0 0',
},
],
{
duration: 240,
easing: 'ease',
fill: 'forwards',
}
);
}
if (e.newState === 'closed') {
animate = popover.animate(
[
{
opacity: 1,
translate: '0 0',
},
{
opacity: 0,
translate: '0 -100%',
},
],
{
duration: 240,
easing: 'ease',
fill: 'forwards',
}
);
}
});
popover.addEventListener('toggle', async (e) => {
console.log('toggle', e);
const isFinished = await animate.finished;
if (e.newState === 'open') {
popover.style.setProperty('--popover-status', 'unset');
}
if (e.newState === 'closed') {
popover.style.setProperty('--popover-status', 'none');
// or
// animate.addEventListener('finish', () => {
// popover.style.setProperty('--popover-status', 'none');
// });
}
});
// or
// const popoverAnimate = {
// 'popover-in'() {
// popover.style.setProperty('--popover-status', 'unset');
// },
// 'popover-out'() {
// popover.style.setProperty('--popover-status', 'none');
// },
// };
// popover.addEventListener('animationend', (e) => {
// popoverAnimate[e.animationName]?.();
// });
</script>
</body>
</html>