此 Codelab 将教您如何在 Web 上构建自适应滑出式侧边导航布局组件。我们将逐步构建该组件,首先是 HTML,然后是 CSS,最后是 JavaScript。
请参阅我的博文构建 Sidenav 组件,了解为构建此组件而选择的 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:
<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>
此内容及其长度会导致网页在超出视口高度时可滚动。
到目前为止,您已经添加了一个 aside 元素,其中包含导航、链接和关闭侧边导航栏的方式。 您还添加了标题、打开侧边导航栏的方式以及主元素中的文章。 这已经很简洁、语义化且相当经典了,但我们可以让它对所有人来说更简洁明了。侧边栏中的打开链接可以标记得更清楚。
向标题打开链接元素添加属性 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
或更小时,隐藏该侧边栏。除非!
向 #sidenav-open
元素添加 :target
伪选择器:
#sidenav-open {
@media (max-width: 540px) {
&:target {
visibility: visible;
}
}
}
当该元素的 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();
});
试试看
- 如需预览网站,请按查看应用。然后按全屏图标
。
总结
以上就是我对组件的需求。您可以随意在此基础上进行构建,使用 JavaScript 状态(而非网址)来驱动它,总之,让它成为您自己的!总会有更多内容需要添加或更多使用情形需要涵盖。
打开 css/brandnav.css
,查看我为此组件应用的相关非布局样式。我认为它对当时我关注的功能集并不重要,并且希望将样式与布局分离能够鼓励复制和粘贴。您可以在那里学到更多知识!
如何制作滑出式自适应侧边导航栏组件? 您是否曾有过多个,例如两侧各有一个?我很乐意在 YouTube 视频中展示您的解决方案,请务必在 Twitter 上提及我或在 YouTube 中评论您的代码,这会对大家有所帮助!