Codelab: การสร้างคอมโพเนนต์ Sidenav

Codelab นี้จะสอนวิธีสร้างคอมโพเนนต์เลย์เอาต์การนำทางด้านข้างแบบสไลด์ที่ปรับเปลี่ยนตามอุปกรณ์บนเว็บ เราจะสร้างคอมโพเนนต์นี้ไปเรื่อยๆ โดยเริ่มจาก HTML จากนั้นตามด้วย CSS และตามด้วย JavaScript

ลองดูบล็อกโพสต์การสร้างคอมโพเนนต์ Sidenav เพื่อเรียนรู้เกี่ยวกับฟีเจอร์แพลตฟอร์มเว็บ CSS ที่เลือกไว้สำหรับการสร้างคอมโพเนนต์นี้

การตั้งค่า

  1. คลิกรีมิกซ์เพื่อแก้ไขเพื่อทำให้โปรเจ็กต์แก้ไขได้
  2. เปิด 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>

เนื้อหานี้รวมถึงความยาวคือสิ่งที่ทำให้หน้าเว็บเลื่อนได้เมื่อเกินความสูงของวิวพอร์ตของคุณ

ตอนนี้คุณได้เพิ่มองค์ประกอบด้านข้างแล้ว โดยมีการนำทาง ลิงก์ และวิธีปิดการนำทางด้านข้าง นอกจากนี้ คุณยังเพิ่มส่วนหัว วิธีเปิดการนำทางด้านข้าง และบทความไปยังองค์ประกอบหลัก ขั้นตอนนี้ดูสะอาดตา สื่อความหมาย และค่อนข้างอยู่เหนือกาลเวลา แต่เราสามารถทำให้เนื้อหาดูสะอาดตาและชัดเจนขึ้นสำหรับทุกคน ลิงก์ที่เปิดอยู่ในแถบนำทางด้านข้างอาจระบุไว้ชัดเจนกว่า

เพิ่มแอตทริบิวต์ title และ aria-label ลงในองค์ประกอบลิงก์เปิดส่วนหัว

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

และยังระบุไอคอน SVG ที่เปิดอยู่ได้ชัดเจนยิ่งขึ้นด้วย เพิ่มแอตทริบิวต์ต่อไปนี้ให้กับ SVG ภายในองค์ประกอบ Open Link

<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

ด้วยฟังก์ชันการซ้อนที่ปรับเปลี่ยนตามอุปกรณ์นี้เป็นฐาน ตอนนี้เราสามารถใช้ประโยชน์จากสถานะของแถบ URL ในการสลับรูปแบบการแสดงและการเปลี่ยนของการนำทางด้านข้างได้

อัปเดตองค์ประกอบ <aside> กลับใน app/index.html โดยทำดังนี้

<aside>
<aside id="sidenav-open">

ซึ่งจะช่วยให้ CSS จับคู่องค์ประกอบและแฮช URL เข้าด้วยกันได้ ข้อมูลนี้สำคัญต่อการใช้งาน :target ตอนนี้รหัสขององค์ประกอบสามารถจับคู่กับแฮช URL ที่เรากำลังจะตั้งค่าด้วยแท็ก <a> แล้ว

นอกจากนี้ เพื่อให้การกำหนดเป้าหมาย JavaScript ง่ายขึ้น ให้เพิ่มรหัสสำหรับองค์ประกอบหลักที่ควบคุมการนำทางด้านข้าง ขั้นแรก เพิ่มรหัสไปยังลิงก์เปิดการนำทางด้านข้าง โดยทำดังนี้

<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">

จากนั้น เพิ่มรหัสไปยังลิงก์ปิดการนำทางด้านข้าง โดยทำดังนี้

<a href="#" title="Close Menu" aria-label="Close Menu"></a>
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

ซึ่งรวมการออกแบบแบบซ้อนที่ปรับเปลี่ยนตามอุปกรณ์ <body> ของมาโคร และเชื่อมโยงเราเข้ากับแถบ URL มาลุยกันต่อเลย

<aside> ยังมีเลย์เอาต์ที่เป็นระเบียบอีกด้วย โดย URL จะมีองค์ประกอบย่อย 2 รายการ ได้แก่ <nav> ซึ่งเป็นองค์ประกอบคล้ายกระดาษที่เลื่อนออก และองค์ประกอบลิงก์ <a> ปิดที่กำหนดให้ URL ต้องเป็น # ลิงก์นี้จะมองไม่เห็นที่ด้านขวาของการนำทางที่เลื่อนออกจากหน้ากระดาษ เพื่อนๆ จึงสามารถ "คลิก" องค์ประกอบภาพเพื่อปิดได้

เพิ่ม 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;
    }
  }
}

เมื่อรหัสขององค์ประกอบนั้นและแถบ URL เหมือนกัน ให้ตั้งค่า 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;
  }
}
การสาธิตการโต้ตอบที่มีและไม่มีการใช้ระยะเวลา โดยอิงตามการค้นหาสื่อ "prefers-reduced-motion"

เพิ่ม JavaScript ใน JavaScript

แป้น Escape ควรปิดเมนู เพิ่ม JS นี้ลงใน js/index.js:

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    document.location.hash = '';
  }
});

ซึ่งจะรอติดตามเหตุการณ์สำคัญในองค์ประกอบ Sidenav หากเป็น Escape ระบบจะตั้งค่าแฮช URL ให้ว่างเปล่า ซึ่งจะทำให้การนำทางด้านข้างออก

UX JS ส่วนถัดไปคือการจัดการโฟกัส ฉันอยากให้การเปิดและปิดเป็นเรื่องง่าย ฉันก็รอให้การนำทางด้านข้างเสร็จสิ้นการเปลี่ยนแปลงบางอย่าง จากนั้นให้ตรวจสอบกับแฮช URL เพื่อดูว่าอยู่ในหรือออก จากนั้นผมใช้ 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 แทน URL และโดยทั่วไปก็ให้เป็นแบบของคุณ! แต่ก็ยังมีสิ่งใหม่ๆ ให้พูดถึงอยู่เสมอ

เปิด css/brandnav.css เพื่อดูรูปแบบที่เกี่ยวข้องกับเลย์เอาต์ที่ฉันใช้ กับคอมโพเนนต์นี้ เราไม่รู้สึกว่าชุดฟีเจอร์ที่ผมเน้นมีความสำคัญ และหวังว่าการแยกรูปแบบออกจากเลย์เอาต์จะช่วยกระตุ้นการคัดลอกและวาง น่าจะมีอะไรให้คุณเรียนรู้มากกว่านี้

คุณจะสร้างคอมโพเนนต์การนำทางด้านข้างที่ปรับเปลี่ยนตามพื้นที่โฆษณาได้อย่างไร คุณเคยมีมากกว่า 1 ด้าน เช่น ด้านใดด้านหนึ่งทั้ง 2 ด้านไหม ฉันอยากจะแสดงวิธีแก้ปัญหาของคุณในวิดีโอ YouTube อย่าลืมทวีตหาฉันหรือแสดงความคิดเห็นใน YouTube พร้อมใส่โค้ดของคุณ เพราะจะช่วยทุกคนได้