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

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

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

การสาธิต

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

ภาพรวม

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

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

UX เบื้องหลัง

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

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

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

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

คอลเล็กชัน

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

รายการ

วิดีโอเกมคือรายการ แต่คอลเล็กชันที่เฉพาะเจาะจงก็อาจเป็นไอเทมได้ด้วยหากแสดงถึงคอลเล็กชันอื่น ตัวอย่างเช่น 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 การนำทางโดยนัย ในการทดสอบ ฉันสังเกตเห็นว่าการมีแอตทริบิวต์ 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> เท่านั้น แต่ผมได้เพิ่ม UX การส่งผ่านด้วยการเลือกที่คลุมเครือ คลาส .crumb มีหน้าที่วางลิงก์และไอคอน ส่วน .crumbicon มีหน้าที่วางไอคอนซ้อนกันและเลือกองค์ประกอบเข้าด้วยกัน ผมเรียกว่า Split Link เพราะฟังก์ชันการทำงานคล้ายกับปุ่มแยกมาก แต่สำหรับการนำทางหน้าเว็บ

<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 แอตทริบิวต์หนึ่งให้บริบทของปุ่มสำหรับผู้ใช้จำนวนมาก

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

การตกแต่งที่คั่น

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

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

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

รูปแบบ

เนื่องจากสีนี้ใช้สีของระบบ ส่วนใหญ่แล้วจึงทำให้เกิดช่องว่างและการซ้อนทับสำหรับรูปแบบ

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

เครื่องมือสำหรับนักพัฒนาเว็บแสดงการปรับแนวการนำทางเบรดครัมบ์พร้อมฟีเจอร์การวางซ้อน 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> ที่ "แทบจะมองไม่เห็น"

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

.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> ด้วยความกระตือรือร้น

จำเป็นต้องมีการป้องกันเหตุการณ์ Eager Event เนื่องจากการใช้องค์ประกอบ <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> เริ่มทำงาน

บทสรุป

ตอนนี้คุณก็รู้แล้วว่าฉันทำท่านั้นได้อย่างไร คุณจะทำยังไงบ้างคะ‽ 🙂

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

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