Theory
查看 pinterest 网站的当前实现(将来可能会改变),当您打开覆盖层时,会出现一个noscroll
类应用于body
元素(设置overflow: hidden
)使body
不再可滚动。
动态创建的覆盖层或已注入页面并通过以下方式可见display: block
— 没什么区别 – has position : fixed
and overflow-y: scroll
, with top
, left
, right
and bottom
属性设置为0
:这种样式使叠加层填充整个视口(但现在我们已经是 2022 年了,所以你可以使用inset: 0
反而)。
The div
覆盖层里面是position: static
所以垂直滚动条与该元素相关。这导致了可滚动但固定的覆盖。
当你关闭覆盖层时,你必须隐藏它(使用display: none
),您甚至可以通过 javascript 删除节点(或仅删除其中的内容,这取决于您,但也取决于内容的性质)。
最后一步也是删除noscroll
类应用于body
(所以overflow
财产恢复到之前的价值)
Code
Codepen 示例 https://codepen.io/fcalderan/pen/qBYyRgE?editors=0010
(它的工作原理是改变aria-hidden
覆盖层的属性,以便显示和隐藏它并增加其可访问性)。
Markup
(打开按钮)
<button type="button" class="open-overlay">OPEN LAYER</button>
(覆盖和关闭按钮)
<section class="overlay" aria-hidden="true" tabindex="-1">
<div>
<h2>Hello, I'm the overlayer</h2>
...
<button type="button" class="close-overlay">CLOSE LAYER</button>
</div>
</section>
CSS
.noscroll {
overflow: hidden;
}
.overlay {
position: fixed;
overflow-y: scroll;
inset: 0; }
[aria-hidden="true"] { display: none; }
[aria-hidden="false"] { display: block; }
JavaScript (原版-JS)
var body = document.body,
overlay = document.querySelector('.overlay'),
overlayBtts = document.querySelectorAll('button[class$="overlay"]'),
openingBtt;
[].forEach.call(overlayBtts, function(btt) {
btt.addEventListener('click', function() {
/* Detect the button class name */
var overlayOpen = this.className === 'open-overlay';
/* storing a reference to the opening button */
if (overlayOpen) {
openingBtt = this;
}
/* Toggle the aria-hidden state on the overlay and the
no-scroll class on the body */
overlay.setAttribute('aria-hidden', !overlayOpen);
body.classList.toggle('noscroll', overlayOpen);
/* On some mobile browser when the overlay was previously
opened and scrolled, if you open it again it doesn't
reset its scrollTop property */
overlay.scrollTop = 0;
/* forcing focus for Assistive technologies but note:
- if your modal has just a phrase and a button move the
focus on the button
- if your modal has a long text inside (e.g. a privacy
policy) move the focus on the first heading inside
the modal
- otherwise just focus the modal.
When you close the overlay restore the focus on the
button that opened the modal.
*/
if (overlayOpen) {
overlay.focus();
}
else {
openingBtt.focus();
openingBtt = null;
}
}, false);
});
/* detect Escape key when the overlay is open */
document.body.addEventListener('keyup', (ev) => {
if (ev.key === "Escape" && overlay.getAttribute('aria-hidden') === 'false') {
overlay.setAttribute('aria-hidden', 'true');
body.classList.toggle('noscroll', false);
openingBtt.focus();
openingBtt = null;
}
})
最后,这是另一个示例,其中叠加层通过 CSS 打开并具有淡入效果transition
应用于opacity
财产。也padding-right
用于避免滚动条消失时底层文本的重排。
Codepen 示例(淡入淡出) https://codepen.io/fcalderan/pen/mdLjRYW?editors=0110
CSS
.noscroll { overflow: hidden; }
@media (min-device-width: 1025px) {
/* not strictly necessary, just an experiment for
this specific example and couldn't be necessary
at all on some browser */
.noscroll {
padding-right: 15px;
}
}
.overlay {
position: fixed;
overflow-y: scroll;
inset: 0;
}
[aria-hidden="true"] {
transition: opacity 1s, z-index 0s 1s;
width: 100vw;
z-index: -1;
opacity: 0;
}
[aria-hidden="false"] {
transition: opacity 1s;
width: 100%;
z-index: 1;
opacity: 1;
}