ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างคอมโพเนนต์ 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>

กำหนดเพียงครั้งเดียว ใช้ได้หลายครั้งตามต้องการโดยมีผลต่อประสิทธิภาพของหน้าเว็บน้อยที่สุด
และจัดรูปแบบได้อย่างยืดหยุ่น โปรดสังเกตว่าระบบได้เพิ่ม aria-hidden="true" ลงในองค์ประกอบ SVG แล้ว
ไอคอนเหล่านี้ไม่มีประโยชน์สำหรับผู้ที่เรียกดูเนื้อหาโดยฟังเพียงอย่างเดียว การซ่อนไอคอนจากผู้ใช้เหล่านั้นจะช่วยไม่ให้เกิดเสียงรบกวนที่ไม่จำเป็น
ลิงก์แยก .crumb
ซึ่งเป็นจุดที่ 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 ซึ่งจะกล่าวถึงในส่วนถัดไปจะช่วยให้การเว้นวรรคเหล่านี้เป็นเรื่องง่าย
รูปแบบ
เนื่องจากสีใช้สีของระบบ จึงส่วนใหญ่จะเป็นช่องว่างและกองสำหรับสไตล์
ทิศทางและโฟลว์ของเลย์เอาต์

องค์ประกอบการนำทางหลัก nav.breadcrumbs จะตั้งค่าพร็อพเพอร์ตี้ที่กำหนดเองที่มีขอบเขต
เพื่อให้องค์ประกอบย่อยใช้ และสร้างเลย์เอาต์แนวนอนที่จัดแนวตั้ง
ซึ่งจะช่วยให้มั่นใจได้ว่าเส้นทางของหน้าเว็บ ตัวคั่น และไอคอนจะสอดคล้องกัน
.breadcrumbs {
--nav-gap: 2ch;
display: flex;
align-items: center;
gap: var(--nav-gap);
padding: calc(var(--nav-gap) / 2);
}

.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 */
}
รายการเพิ่มเติม
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> ทริกเกอร์
บทสรุป
ตอนนี้คุณรู้วิธีที่ฉันใช้แล้ว คุณจะทำอย่างไร 🙂
มาลองใช้แนวทางที่หลากหลายและเรียนรู้วิธีต่างๆ ในการสร้างสรรค์บนเว็บกัน สร้างการสาธิต ทวีตลิงก์มาให้ฉัน แล้วฉันจะเพิ่มลิงก์นั้น ลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- Tux Solbakk เป็นคอมโพเนนต์ของเว็บ: การสาธิตและโค้ด