在此 Codelab 中,您将学习如何在 Web 上构建自适应滑出式侧边导航布局组件。我们将逐步构建该组件,首先是 HTML,然后是 CSS,最后是 JavaScript。
请参阅我的博文构建侧边栏组件,了解为构建此组件而选择的 CSS Web 平台功能。
设置
- 点击 Remix to Edit 即可修改项目。
- 打开
app/index.html
。
HTML
首先,了解 HTML 设置的基本知识,以便有内容和一些框可用。
将以下 HTML 代码放入 <body>
标记中。
<aside></aside>
<main></main>
<aside>
用于包含导航菜单,作为 <main>
(用于包含主要页面内容)的补充元素。
接下来,我们将使用网页的其余内容填充这些语义元素。
在 <aside>
元素中添加一个导航元素、一些导航链接和一个关闭链接。
<aside>
<nav>
<h4>My</h4>
<a href="#">Dashboard</a>
<a href="#">Profile</a>
<a href="#">Preferences</a>
<a href="#">Archive</a>
<h4>Settings</h4>
<a href="#">Accessibility</a>
<a href="#">Theme</a>
<a href="#">Admin</a>
</nav>
<a href="#"></a>
</aside>
链接非常适合在 <nav>
元素中使用,而 <nav>
元素非常适合在 <aside>
边栏中使用。不过,我们仍有许多改进空间。
在主要内容元素中,添加标题和文章以语义上容纳布局内容。
<main>
<header>
<a href="#sidenav-open" class="hamburger">
<svg viewBox="0 0 50 40">
<line x1="0" x2="100%" y1="10%" y2="10%" />
<line x1="0" x2="100%" y1="50%" y2="50%" />
<line x1="0" x2="100%" y1="90%" y2="90%" />
</svg>
</a>
<h1>Site Title</h1>
</header>
<article>
{put some placeholder content here}
</article>
</main>
标题中包含菜单打开链接。边栏中包含关闭按钮。我们很快就会根据视口大小显示和隐藏元素。
在 <article>
元素中,我们粘贴了一个占位符句子。将“”替换为您自己的内容,或粘贴下面提供的 Lorem ipsum:
<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
当此类内容及其长度超出视口高度时,会导致网页可滚动。
到目前为止,您已添加了侧边栏元素,其中包含导航栏、链接和关闭侧边栏的方法。 您还在主要元素中添加了标题、用于打开侧边栏的方法和一篇文章。 这已经很简洁、清晰且不易过时了,但我们可以让它变得更加简洁明了,让所有人都能看懂。边栏中的打开链接可以更明确地标记出来。
向标题打开链接元素添加 title
和 aria-label
属性:
<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">
打开的 SVG 图标也可以更明确地标记。 将以下属性添加到打开的链接元素内的 SVG:
<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">
边栏中的关闭链接可以标记得更清晰。将属性 title
和 aria-label
添加到侧边栏关闭链接元素:
<a href="#"></a>
<a href="#" title="Close Menu" aria-label="Close Menu"></a>
CSS
现在来排列元素。主要内容和侧边栏是 <body>
标记的直接子元素,因此从这里入手是个不错的选择。
将以下 CSS 添加到 css/sidenav.css
中,以便 <body>
元素排列子元素。
body {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
@media (max-width: 540px) {
& > :matches(aside, main) {
grid-area: stack;
}
}
}
此布局的本质是:创建一个包含所有内容且名为 stack
的行,并在该行中创建 2 个列,其中第 2 个列也名为 stack
。第 1 列的大小应根据其最小内容需求确定,第 2 列可占用其余空间。然后,如果在限制为 540px
或更小的视口中,请将侧边导航栏和主要内容元素放入同一行和列中,使其在 1x1 网格中彼此堆叠。
以此响应式堆叠功能为基础,我们现在可以利用网址栏的状态来切换侧边栏的可见性和转换样式。
在 app/index.html
中重新更新 <aside>
元素:
<aside>
<aside id="sidenav-open">
这样,CSS 便可将元素与网址哈希一起匹配。这对于 :target
的使用至关重要。现在,该元素的 ID 可以与我们将使用 <a>
标记设置的网址哈希匹配。
此外,为了更轻松地定位 JavaScript,请为控制侧边栏的主要元素添加 ID。首先,为侧边栏打开链接添加 ID:
<a href="#sidenav-open" class="hamburger" title="Open Menu" aria-label="Open Menu">
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
接下来,为侧边栏关闭链接添加 ID:
<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
至此,宏 <body>
响应式堆叠布局已完成,并且我们已与网址栏相关联。
让我们继续构建吧!
<aside>
的布局也很整洁。它有 2 个子元素,一个是 <nav>
,即滑出的纸张式组件,另一个是用于将网址设置为 #
的闭合 <a>
链接元素。该链接位于纸质侧边滑动导航栏右侧,不可见;这样用户就可以“点击关闭”视觉组件来关闭它。
将以下 CSS 添加到 css/sidenav.css
:
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
我认为比例和名称非常出色,网格在这里可以发挥出色作用,并为设计师提供大量控制功能。
接下来,我需要有条件地叠加主内容,并在文档滚动时保留我的位置。这对于 position: sticky
和某些 overscroll-behavior
来说非常有用。
为侧边栏添加以下样式:
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
@media (max-width: 540px) {
position: sticky;
top: 0;
max-height: 100vh;
overflow: hidden auto;
overscroll-behavior: contain;
visibility: hidden; /* not keyboard accessible when closed */
}
}
这些样式可确保侧边栏的高度与视口高度相同,并会垂直滚动并包含滚动条。非常重要的是,它会隐藏元素。默认情况下,当视口小于或等于 540px
时,系统会隐藏该侧边栏。除非!
将 :target
伪类选择器添加到 #sidenav-open
元素:
#sidenav-open {
@media (max-width: 540px) {
&:target {
visibility: visible;
}
}
}
如果该元素的 ID 与网址栏的 ID 相同,请将 visibility
设置为 visible
。滚动页面后,请继续打开侧边菜单,或尝试在侧边菜单处于打开状态时滚动页面。您有何看法?
将以下 CSS 添加到 app/sidenav.css
的底部:
#sidenav-button,
#sidenav-close {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
user-select: none;
touch-action: manipulation;
@media (min-width: 540px) {
display: none;
}
}
这些样式定位到我们的“打开”和“关闭”按钮,指定了它们的点按和触摸样式,并且在视口大小等于或大于 540px
时会隐藏它们。
为了增添一些亮点,我们来添加一些兼顾无障碍功能的 CSS 转换。
将以下 CSS 添加到 css/sidenav.css
:
#sidenav-open {
--easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
--duration: .6s;
...
@media (max-width: 540px) {
...
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
&:target {
visibility: visible;
transform: translateX(0);
transition: transform var(--duration) var(--easeOutExpo);
}
}
@media (prefers-reduced-motion: reduce) {
--duration: 1ms;
}
}
添加一些 JavaScript
Escape
键应会关闭菜单。将以下 JS 添加到 js/index.js
:
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', e => {
if (e.code === 'Escape') {
document.location.hash = '';
}
});
这会监听侧边栏元素上的按键事件。如果为 Escape
,则会将网址哈希设置为空,使侧边栏转换为非活动状态。
接下来介绍的 UX JS 部分是焦点管理。为了简化打开和关闭操作,我会等到侧边栏完成某种转换,然后与网址哈希进行交叉检查,以确定它是处于打开还是关闭状态。然后,我使用 JavaScript 将焦点设置为与用户刚刚按下的按钮相辅相成。
将以下 JavaScript 添加到 js/index.js
:
const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');
sidenav.addEventListener('transitionend', e => {
if (e.propertyName !== 'transform') {
return;
}
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? closenav.focus()
: opennav.focus();
});
试试看
- 如需预览网站,请按 View App(查看应用)。然后按 Fullscreen(全屏)。
总结
以上就是我对该组件的需求总结。您可以随意在此基础上进行构建,使用 JavaScript 状态(而非网址)驱动它,并根据自己的需求进行使用!我们还会不断添加更多内容或涵盖更多用例。
打开 css/brandnav.css
查看我应用于此组件的非布局相关样式。我认为这对我关注的功能集来说并不重要,并且希望将样式与布局分离,以鼓励用户复制和粘贴。您可以从中学习到更多知识!
如何制作滑出式自适应侧边栏组件? 您是否有过 1 个以上的耳鸣,例如两侧各有一个?我很乐意在 YouTube 视频中展示您的解决方案,请务必在推特上向我发推文或在 YouTube 中发表评论并附上代码,这对大家来说都很有帮助!