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