การสร้างคอมโพเนนต์เบรดครัมบ์

ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างคอมโพเนนต์ Breadcrumb ที่ตอบสนองและเข้าถึงได้เพื่อให้ผู้ใช้ไปยังส่วนต่างๆ ของเว็บไซต์

ในโพสต์นี้ ฉันต้องการแชร์แนวคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์ Breadcrumb ลองใช้เดโม

การสาธิต

หากต้องการดูวิดีโอ โปรดดูโพสต์นี้ใน YouTube

ภาพรวม

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

เบรดครัมบ์ในโพสต์นี้ไม่ใช่เบรดครัมบ์มาตรฐาน แต่เป็นเบรดครัมบ์ที่คล้ายกัน โดยจะเพิ่มฟังก์ชันการทำงานเพิ่มเติมด้วยการใส่หน้าย่อย ลงในการนำทางโดยใช้ <select> ซึ่งจะช่วยให้เข้าถึงได้หลายระดับ

UX เบื้องหลัง

ในวิดีโอการสาธิตคอมโพเนนต์ด้านบน หมวดหมู่ตัวยึดตำแหน่งคือประเภทของ วิดีโอเกม ระบบจะสร้างเส้นทางนี้โดยไปที่เส้นทาง home » rpg » indie » on sale ดังที่แสดงด้านล่าง

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

สถาปัตยกรรมข้อมูล

เราคิดว่าการคิดในแง่ของคอลเล็กชันและรายการต่างๆ จะช่วยได้

คอลเล็กชัน

คอลเล็กชันคืออาร์เรย์ของตัวเลือกให้เลือก จากหน้าแรกของ ต้นแบบเส้นทางของโพสต์นี้ คอลเล็กชันต่างๆ ได้แก่ FPS, RPG, Brawler, Dungeon Crawler, กีฬา และปริศนา

รายการ

วิดีโอเกมคือไอเทม ส่วนคอลเล็กชันที่เฉพาะเจาะจงก็อาจเป็นไอเทมได้เช่นกันหาก แสดงถึงคอลเล็กชันอื่น เช่น RPG เป็นไอเทมและคอลเล็กชันที่ถูกต้อง หากเป็นไอเทม ผู้ใช้จะอยู่ในหน้าคอลเล็กชันนั้น เช่น อยู่ในหน้า RPG ซึ่งแสดงรายการเกม RPG รวมถึง หมวดหมู่ย่อยเพิ่มเติม AAA, Indie และ Self Published

ในแง่ของวิทยาการคอมพิวเตอร์ คอมโพเนนต์เส้นทางนี้แสดงถึงอาร์เรย์ หลายมิติ

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

แอปหรือเว็บไซต์ของคุณจะมีสถาปัตยกรรมข้อมูล (IA) ที่กำหนดเองซึ่งสร้างอาร์เรย์หลายมิติที่แตกต่างกัน แต่ฉันหวังว่าแนวคิดของหน้า Landing Page ของคอลเล็กชันและการข้ามลำดับชั้นจะสามารถนำไปใช้ใน Breadcrumb ของคุณได้เช่นกัน

เลย์เอาต์

Markup

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

รูปแบบสีเข้มและสีอ่อน

<meta name="color-scheme" content="dark light">

เมตาแท็ก color-scheme ในข้อมูลโค้ดด้านบนจะแจ้งให้เบราว์เซอร์ทราบว่าหน้านี้ต้องการสไตล์เบราว์เซอร์แบบสว่างและมืด ขนมปังนำทางตัวอย่างไม่มี CSS สำหรับรูปแบบสีเหล่านี้ ดังนั้นขนมปังนำทางจะใช้สีเริ่มต้นที่เบราว์เซอร์ระบุ

<nav class="breadcrumbs" role="navigation"></nav>

คุณควรใช้องค์ประกอบ <nav> สำหรับการนำทางเว็บไซต์ ซึ่งมี role ของ navigation ARIA โดยนัย ในการทดสอบ เราพบว่าการมีแอตทริบิวต์ role เปลี่ยนวิธีที่โปรแกรมอ่านหน้าจอโต้ตอบกับองค์ประกอบ โดยจะประกาศเป็น การนำทาง ดังนั้นเราจึงเลือกที่จะเพิ่มแอตทริบิวต์นี้

ไอคอน

เมื่อไอคอนปรากฏซ้ำในหน้าเว็บ องค์ประกอบ SVG <use> หมายความว่าคุณกำหนด path ได้ครั้งเดียว และใช้กับอินสแตนซ์ทั้งหมดของไอคอนได้ ซึ่งจะช่วยป้องกันไม่ให้ข้อมูลเส้นทางเดียวกันซ้ำกัน ซึ่งอาจทำให้เอกสารมีขนาดใหญ่ขึ้นและอาจเกิดความไม่สอดคล้องกันของเส้นทาง

หากต้องการใช้เทคนิคนี้ ให้เพิ่มองค์ประกอบ SVG ที่ซ่อนไว้ในหน้าเว็บและห่อหุ้มไอคอน ในองค์ประกอบ <symbol> ที่มีรหัสที่ไม่ซ้ำกัน

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

เบราว์เซอร์จะอ่าน HTML ของ SVG ใส่ข้อมูลไอคอนลงในหน่วยความจำ และ ดำเนินการต่อกับส่วนที่เหลือของหน้าเว็บโดยอ้างอิงรหัสสำหรับการใช้ไอคอนเพิ่มเติม เช่น

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

DevTools แสดงองค์ประกอบ SVG ที่แสดงผล

กำหนดเพียงครั้งเดียว ใช้ได้หลายครั้งตามต้องการโดยมีผลต่อประสิทธิภาพของหน้าเว็บน้อยที่สุด และจัดรูปแบบได้อย่างยืดหยุ่น โปรดสังเกตว่าระบบได้เพิ่ม aria-hidden="true" ลงในองค์ประกอบ SVG แล้ว ไอคอนเหล่านี้ไม่มีประโยชน์สำหรับผู้ที่เรียกดูเนื้อหาโดยฟังเพียงอย่างเดียว การซ่อนไอคอนจากผู้ใช้เหล่านั้นจะช่วยไม่ให้เกิดเสียงรบกวนที่ไม่จำเป็น

ซึ่งเป็นจุดที่ Breadcrumb แบบดั้งเดิมและ Breadcrumb ในคอมโพเนนต์นี้แตกต่างกัน โดยปกติแล้ว นี่จะเป็นเพียง<a>ลิงก์ แต่ฉันได้เพิ่ม UX การข้ามด้วย การเลือกที่ซ่อนไว้ คลาส .crumb มีหน้าที่จัดวางลิงก์และไอคอน ส่วน .crumbicon มีหน้าที่ซ้อนไอคอนและองค์ประกอบเลือกเข้าด้วยกัน ฉันเรียกฟีเจอร์นี้ว่าลิงก์แยกเนื่องจากฟังก์ชันการทำงานคล้ายกับปุ่มแยก แต่ใช้สำหรับการไปยังส่วนต่างๆ ของหน้าเว็บ

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

ลิงก์และตัวเลือกบางอย่างอาจดูไม่พิเศษ แต่จะช่วยเพิ่มฟังก์ชันการทำงานให้กับ Breadcrumb แบบง่ายๆ การเพิ่ม title ลงในองค์ประกอบ <select> จะเป็นประโยชน์สำหรับผู้ใช้โปรแกรมอ่านหน้าจอ โดยจะให้ข้อมูลเกี่ยวกับการทำงานของปุ่ม แต่จะให้ความช่วยเหลือแบบเดียวกันกับคนอื่นๆ ด้วย โดยคุณจะเห็นฟีเจอร์นี้อยู่ตรงกลางด้านหน้าบน iPad แอตทริบิวต์เดียวจะให้บริบทของปุ่มแก่ผู้ใช้จำนวนมาก

ภาพหน้าจอที่มีองค์ประกอบที่เลือกที่มองไม่เห็นเมื่อวางเมาส์เหนือองค์ประกอบนั้น และ
เคล็ดลับเครื่องมือตามบริบทที่แสดง

การตกแต่งตัวคั่น

<span class="crumb-separator" aria-hidden="true">→</span>

คุณจะใช้หรือไม่ใช้ตัวคั่นก็ได้ หรือจะใช้เพียงตัวเดียวก็ได้ (ดูตัวอย่างที่ 3 ในวิดีโอด้านบน) จากนั้นฉันจะให้แต่ละ aria-hidden="true" เนื่องจากเป็นองค์ประกอบตกแต่งและไม่ใช่ สิ่งที่โปรแกรมอ่านหน้าจอต้องประกาศ

พร็อพเพอร์ตี้ gap ซึ่งจะกล่าวถึงในส่วนถัดไปจะช่วยให้การเว้นวรรคเหล่านี้เป็นเรื่องง่าย

รูปแบบ

เนื่องจากสีใช้สีของระบบ จึงส่วนใหญ่จะเป็นช่องว่างและกองสำหรับสไตล์

ทิศทางและโฟลว์ของเลย์เอาต์

DevTools แสดงการจัดแนวการนำทางเบรดครัมบ์ด้วยฟีเจอร์การซ้อนทับ Flexbox

องค์ประกอบการนำทางหลัก nav.breadcrumbs จะตั้งค่าพร็อพเพอร์ตี้ที่กำหนดเองที่มีขอบเขต เพื่อให้องค์ประกอบย่อยใช้ และสร้างเลย์เอาต์แนวนอนที่จัดแนวตั้ง ซึ่งจะช่วยให้มั่นใจได้ว่าเส้นทางของหน้าเว็บ ตัวคั่น และไอคอนจะสอดคล้องกัน

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

แสดงเบรดครัมบ์ 1 รายการที่จัดแนวตั้งด้วยการซ้อนทับ Flexbox

.crumb แต่ละรายการยังสร้างเลย์เอาต์ที่จัดแนวในแนวนอนและแนวตั้งโดยมีช่องว่าง แต่จะกำหนดเป้าหมายไปยังลิงก์ย่อยและระบุรูปแบบ white-space: nowrap โดยเฉพาะ ซึ่งเป็นสิ่งสำคัญสำหรับเบรดครัมบ์แบบหลายคำ เนื่องจากเราไม่ต้องการให้เบรดครัมบ์แสดงในหลายบรรทัด ในส่วนท้ายของโพสต์นี้ เราจะเพิ่มสไตล์เพื่อจัดการการล้นแนวนอนที่พร็อพเพอร์ตี้ white-space นี้ทำให้เกิด

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

ระบบจะเพิ่ม aria-current="page" เพื่อช่วยให้ลิงก์หน้าปัจจุบันโดดเด่นจากลิงก์อื่นๆ นอกจากผู้ใช้โปรแกรมอ่านหน้าจอจะมีตัวบ่งชี้ที่ชัดเจนว่าลิงก์นั้น สำหรับหน้าเว็บปัจจุบันแล้ว เรายังได้จัดรูปแบบองค์ประกอบด้วยภาพเพื่อช่วยให้ผู้ใช้ที่มองเห็น ได้รับประสบการณ์การใช้งานที่คล้ายกันด้วย

คอมโพเนนต์ .crumbicon ใช้ตารางกริดเพื่อซ้อนไอคอน SVG กับองค์ประกอบ <select> ที่ "แทบจะมองไม่เห็น"

เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของ Grid แสดงปุ่มซ้อนทับที่ทั้งแถวและคอลัมน์
มีชื่อว่า Stack

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

องค์ประกอบ <select> อยู่สุดท้ายใน DOM จึงอยู่ด้านบนของสแต็ก และโต้ตอบได้ เพิ่มรูปแบบของ opacity: .01 เพื่อให้ยังคงใช้องค์ประกอบได้ และผลลัพธ์คือกล่องตัวเลือกที่พอดีกับรูปร่างของไอคอน วิธีนี้เป็นวิธีที่ดีในการปรับแต่งรูปลักษณ์ขององค์ประกอบ <select> ในขณะที่ ยังคงฟังก์ชันการทำงานในตัวไว้

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

รายการเพิ่มเติม

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

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

รูปแบบการล้นจะตั้งค่า UX ต่อไปนี้

  • การเลื่อนในแนวนอนที่มีการจำกัดการเลื่อนเกิน
  • ระยะห่างจากขอบของการเลื่อนแนวนอน
  • จุดสแนป 1 จุดบนเส้นทางสุดท้าย ซึ่งหมายความว่าเมื่อโหลดหน้าเว็บ เส้นทางแรกจะโหลดและแสดงในมุมมอง
  • นำจุดสแนปออกจาก Safari ซึ่งมีปัญหาเกี่ยวกับการเลื่อนแนวนอน และการผสมผสานเอฟเฟกต์สแนป

การค้นหาสื่อ

การปรับเปลี่ยนเล็กๆ น้อยๆ อย่างหนึ่งสำหรับ Viewport ขนาดเล็กคือการซ่อนป้ายกำกับ "หน้าแรก" โดยเหลือไว้เพียงไอคอน ดังนี้

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

การเปรียบเทียบเส้นทางแบบมีและไม่มีป้ายกำกับหน้าแรก

การช่วยเหลือพิเศษ

การเคลื่อนไหว

คอมโพเนนต์นี้มีการเคลื่อนไหวไม่มากนัก แต่การรวมการเปลี่ยนผ่านไว้ในการตรวจสอบ prefers-reduced-motion จะช่วยป้องกันการเคลื่อนไหวที่ไม่ต้องการได้

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

ไม่จำเป็นต้องเปลี่ยนสไตล์อื่นๆ เอฟเฟกต์การวางเมาส์และการโฟกัสก็ดี และมีความหมายโดยไม่ต้องมี transition แต่หากใช้การเคลื่อนไหวได้ เราจะเพิ่มทรานซิชันเล็กๆ น้อยๆ ลงในการโต้ตอบ

JavaScript

ก่อนอื่น ไม่ว่าคุณจะใช้เราเตอร์ประเภทใดในเว็บไซต์หรือแอปพลิเคชัน เมื่อผู้ใช้เปลี่ยน Breadcrumb คุณจะต้องอัปเดต URL และแสดงหน้าเว็บที่เหมาะสมแก่ผู้ใช้ ประการที่ 2 เพื่อให้ประสบการณ์ของผู้ใช้เป็นไปตามปกติ โปรดตรวจสอบว่า ไม่มีการนำทางที่ไม่คาดคิดเกิดขึ้นเมื่อผู้ใช้เพียงแค่เรียกดู<select> ตัวเลือก

มาตรการด้านประสบการณ์ของผู้ใช้ที่สำคัญ 2 อย่างที่ JavaScript ต้องจัดการ ได้แก่ การเลือก has changed และการป้องกันการทริกเกอร์เหตุการณ์การเปลี่ยนแปลง <select> eager

การป้องกันเหตุการณ์ที่กระตือรือร้นเป็นสิ่งจำเป็นเนื่องจากการใช้องค์ประกอบ <select> ใน Windows Edge และอาจรวมถึงเบราว์เซอร์อื่นๆ ด้วย เหตุการณ์ select changed จะทริกเกอร์เมื่อผู้ใช้เรียกดูตัวเลือกด้วยแป้นพิมพ์ ด้วยเหตุนี้ฉันจึงเรียกการดำเนินการนี้ว่า "กระตือรือร้น" เนื่องจากผู้ใช้เพียงแค่เลือกตัวเลือกแบบเสมือน เช่น การวางเมาส์เหนือหรือโฟกัส แต่ยังไม่ได้ยืนยันตัวเลือกด้วย enter หรือ click เหตุการณ์ที่กระตือรือร้นทำให้ฟีเจอร์การเปลี่ยนหมวดหมู่คอมโพเนนต์นี้เข้าถึงไม่ได้ เนื่องจาก การเปิดกล่องตัวเลือกและการเรียกดูรายการเพียงอย่างเดียวจะทริกเกอร์เหตุการณ์และ เปลี่ยนหน้าก่อนที่ผู้ใช้จะพร้อม

<select> กิจกรรมที่มีการเปลี่ยนแปลงที่ดีขึ้น

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

กลยุทธ์สำหรับเรื่องนี้คือการดูเหตุการณ์กดแป้นพิมพ์ลงในแต่ละองค์ประกอบ <select> และพิจารณาว่าแป้นที่กดเป็นการยืนยันการนำทาง (Tab หรือ Enter) หรือการนำทางเชิงพื้นที่ (ArrowUp หรือ ArrowDown) เมื่อพิจารณาแล้ว คอมโพเนนต์จะตัดสินใจได้ว่าจะรอหรือไปต่อเมื่อเหตุการณ์สำหรับองค์ประกอบ <select> ทริกเกอร์

บทสรุป

ตอนนี้คุณรู้วิธีที่ฉันใช้แล้ว คุณจะทำอย่างไร 🙂

มาลองใช้แนวทางที่หลากหลายและเรียนรู้วิธีต่างๆ ในการสร้างสรรค์บนเว็บกัน สร้างการสาธิต ทวีตลิงก์มาให้ฉัน แล้วฉันจะเพิ่มลิงก์นั้น ลงในส่วนรีมิกซ์ของชุมชนด้านล่าง

รีมิกซ์ของชุมชน