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

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

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

สาธิต

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

ภาพรวม

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

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

UX เบื้องหลัง

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

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

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

ฉันคิดว่าการมองในแง่ของคอลเล็กชันและรายการต่างๆ มีประโยชน์มาก

คอลเล็กชัน

คอลเล็กชันคืออาร์เรย์ของตัวเลือกที่มีให้เลือก จากหน้าแรกของต้นแบบเบรดครัมบ์ของโพสต์นี้ จะเห็นว่ามีคอลเล็กชัน FPS, RPG, เกมต่อสู้, เกมผจญภัยในดันเจี้ยน, กีฬา และปริศนา

รายการ

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

ในภาษาวิทยาการคอมพิวเตอร์ คอมโพเนนต์เบรดครัมบ์นี้แสดงถึงอาร์เรย์มัลติไดเมนชัน

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

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

เลย์เอาต์

Markup

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

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

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

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

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

คุณควรใช้องค์ประกอบ <nav> สำหรับการนําทางเว็บไซต์ ซึ่งมีบทบาท ARIA ของ 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>

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

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

เครื่องมือสำหรับนักพัฒนาเว็บแสดงองค์ประกอบการใช้ SVG ที่แสดงผล

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

ตรงนี้เป็นจุดที่เบรดครัมบ์แบบดั้งเดิมและเบรดครัมบ์ในคอมโพเนนต์นี้แตกต่างกัน โดยปกตินี่จะเป็นลิงก์ <a> เท่านั้น แต่ผมได้เพิ่ม Traversal 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>

ลิงก์และตัวเลือกบางอย่างนั้นไม่ได้มีอะไรพิเศษ แต่ช่วยเพิ่มฟังก์ชันการทำงานให้กับเบรดครัมบ์ธรรมดา การเพิ่ม 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 แต่ละรายการจะสร้างเลย์เอาต์ในแนวตั้งแบบแนวนอนด้วย gap แต่มุ่งเป้าไปที่เด็กของลิงก์เองและระบุรูปแบบ 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> ที่ "แทบจะมองไม่เห็น"

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

.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 */
}

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

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

.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 ซึ่งมีปัญหากับการเลื่อนแนวนอนและเอฟเฟกต์การยึด

คิวรีสื่อ

การปรับเล็กๆ น้อยๆ สำหรับวิวพอร์ตที่เล็กลงคือการซ่อน "หน้าแรก" ป้ายกำกับ, ออก เพียงไอคอน:

@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

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

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

ต้องใช้การป้องกันเหตุการณ์ที่รอดำเนินการเนื่องจากมีการใช้องค์ประกอบ <select> ใน Windows Edge และอาจรวมถึงเบราว์เซอร์อื่นๆ ด้วย เหตุการณ์ 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> เริ่มทำงาน

บทสรุป

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

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

รีมิกซ์ในชุมชน