ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างคอมโพเนนต์เบรดครัมบ์ที่ตอบสนองและเข้าถึงได้เพื่อให้ผู้ใช้ไปยังส่วนต่างๆ ของเว็บไซต์
ในโพสต์นี้ เราต้องการแชร์แนวคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์เบรดครัมบ์ ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์เวอร์ชัน 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>
กำหนดเพียงครั้งเดียว ใช้ได้หลายครั้งตามต้องการ โดยที่ส่งผลกระทบต่อประสิทธิภาพหน้าเว็บน้อยที่สุดและจัดสไตล์ได้อย่างยืดหยุ่น ระบบจะเพิ่มการแจ้งเตือน 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 แอตทริบิวต์หนึ่งให้บริบทของปุ่มแก่ผู้ใช้หลายคน
การตกแต่งตัวคั่น
<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;
}}
}
สไตล์การแสดงผลที่ overflow จะสร้าง UX ดังต่อไปนี้
- การเลื่อนในแนวนอนที่มีการจำกัดการเลื่อนเกิน
- ระยะห่างจากขอบแนวนอน
- จุดยึด 1 จุดบนเศษข้อมูลสุดท้าย ซึ่งหมายความว่าเมื่อโหลดหน้าเว็บ Crumb แรกจะโหลดและแสดงในมุมมอง
- นำจุดยึดออกจาก 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 เป็นคอมโพเนนต์เว็บ: เดโมและโค้ด