程式碼研究室:建構 Sidenav 元件

本程式碼研究室會說明如何在網路上建構回應式滑出式側邊導覽版面配置元件。建構元件時,我們會先從 HTML、CSS 和 JavaScript 開始建構。

請參閱我的網誌文章「建立 Sidenav 元件」,瞭解為建構此元件而選擇的 CSS 網路平台功能。

設定

  1. 按一下「Remix to Edit」,即可編輯專案。
  2. 開啟 app/index.html

HTML

首先,請取得 HTML 設定的必要元素,以便使用內容和一些方塊。

將下列 HTML 程式碼拖曳到 <body> 標記中。

<aside></aside>
<main></main>

<aside> 會保留導覽選單,做為 <main> 的輔助元素,<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>

標頭含有開啟選單的連結。aside 有關閉按鈕。我們很快就會根據可視區域大小顯示或隱藏元素。

我們在 <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>

當這類內容和長度超出可視區域高度時,就會導致網頁需要捲動。

目前您已新增一個側邊元素,其中包含導覽和連結,以及用於關閉側邊導覽列的方法。 您也新增了標題、開啟側邊導覽列的方式,以及主要元素中的文章。這項功能已具備清晰的語意,且永不過時,但我們可以讓它更清晰明確,讓所有人都看得更清楚。側邊導覽列中的開啟連結可以標示得更清楚。

在標頭開啟連結元素中加入屬性 titlearia-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">

側邊導覽列中的關閉連結可以標示得更清楚。在側邊導覽關閉連結元素中加入 titlearia-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。第一欄應根據最少的內容需求調整規模,第 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/sidenav.css 中新增以下 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/sidenav.css 中新增以下 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;
  }
}
以 `prefers-reduced-motion` 媒體查詢為依據,展示有無時間長度限制的互動情形。

加入一些 JavaScript

Escape 鍵應會關閉選單。在 js/index.js 中加入以下 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 上留言加上您的程式碼,對大家都有幫助!