ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์เบรดครัมบ์ที่ตอบสนองและเข้าถึงง่ายเพื่อให้ผู้ใช้ไปยังส่วนต่างๆ ในเว็บไซต์ของคุณได้
ในโพสต์นี้ ฉันอยากแชร์วิธีคิดวิธีสร้างคอมโพเนนต์เบรดครัมบ์ ลองใช้เดโม
หากชอบวิดีโอ นี่คือโพสต์นี้เวอร์ชัน 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 โดยนัย
จากการทดสอบ ผมสังเกตเห็นว่าแอตทริบิวต์ 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>
โปรดกำหนดเพียงครั้งเดียว ใช้งานกี่ครั้งก็ได้ โดยมีผลกระทบต่อประสิทธิภาพของหน้าเว็บน้อยที่สุด
และการจัดรูปแบบที่ยืดหยุ่น การแจ้งเตือน aria-hidden="true"
จะได้รับการเพิ่มลงในองค์ประกอบ SVG
ไอคอนจะไม่มีประโยชน์ต่อผู้ที่เรียกดูเฉพาะผู้ที่ได้ยินเนื้อหาเท่านั้น การซ่อนผู้ใช้จากผู้ใช้เหล่านั้นจะทำให้ผู้ใช้ไม่เพิ่มสัญญาณรบกวนที่ไม่จำเป็น
แยกลิงก์ .crumb
ซึ่งเป็นจุดที่เบรดครัมบ์แบบดั้งเดิมและรายการในคอมโพเนนต์นี้จะแยกออกจากกัน
ปกติแล้วนี่เป็นเพียงลิงก์ <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>
ลิงก์และตัวเลือกบางอย่างไม่มีอะไรพิเศษ แต่จะเพิ่มฟังก์ชันการทำงานให้กับเบรดครัมบ์ที่เรียบง่าย การเพิ่ม title
ลงในองค์ประกอบ <select>
มีประโยชน์สำหรับผู้ใช้โปรแกรมอ่านหน้าจอ ซึ่งเป็นการให้ข้อมูลเกี่ยวกับการทำงานของปุ่ม แต่วิธีนี้ก็มอบความช่วยเหลือแบบเดียวกันแก่คนอื่นๆ ด้วย คุณจะเห็นฟีเจอร์นี้แสดงอยู่ด้านหน้า
บน iPad แอตทริบิวต์ 1 รายการให้บริบทของปุ่มแก่ผู้ใช้จำนวนมาก
การตกแต่งเส้นแบ่ง
<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 */
}
รายการเพิ่มเติม
เบรดครัมบ์ควรแสดงถึงเส้นทางที่ยาวมาก ผมชอบที่ปล่อยให้ สิ่งต่างๆ หายไปในหน้าจอแนวนอนเมื่อเหมาะสม และผมคิดว่าคอมโพเนนต์เบรดครัมบ์นี้เหมาะสมแล้ว
.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 ต่อไปนี้
- การเลื่อนในแนวนอนโดยมีการป้องกันการเลื่อนเกิน
- ระยะห่างจากขอบของการเลื่อนแนวนอน
- สแนปจุดเดียวที่เศษส่วนสุดท้าย ซึ่งหมายความว่าเมื่อโหลดหน้าเว็บ ครัมบ์แรกจะโหลดโดยสแนปและในมุมมอง
- นำจุดในการสแนปออกจาก 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>
เพื่อประสบการณ์การใช้งานของผู้ใช้ให้เป็นปกติ
มาตรการด้านประสบการณ์ของผู้ใช้ที่สำคัญ 2 อย่างที่ JavaScript จัดการ ได้แก่ การเลือกได้เปลี่ยนแปลงและป้องกันการเริ่มทำงานของเหตุการณ์การเปลี่ยนแปลง <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>
เริ่มทำงาน
บทสรุป
ตอนนี้คุณก็รู้แล้วว่าตัวเองทำยังไง คุณจะทำอะไรบ้าง‽ 🙂
มาลองเปลี่ยนแนวทางของเราและเรียนรู้วิธีทั้งหมดเพื่อสร้างเว็บกันเถอะ สร้างเดโม ลิงก์ทวีตฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- Tux Solbakk เป็นคอมโพเนนต์ของเว็บ: การสาธิตและโค้ด