ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างแถบนำทางด้านข้างแบบสไลด์ออกที่ปรับเปลี่ยนตามอุปกรณ์
ในโพสต์นี้ ฉันอยากแชร์วิธีสร้างต้นแบบคอมโพเนนต์ Sidenav สำหรับเว็บที่ปรับเปลี่ยนตามพื้นที่โฆษณา มีสถานะ รองรับการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ ทำงานได้ทั้งแบบมีและไม่มี JavaScript และทำงานได้ในเบราว์เซอร์ต่างๆ ลองใช้เดโม
หากคุณชอบดูวิดีโอมากกว่า เรามีโพสต์นี้ในเวอร์ชัน YouTube ด้วย
ภาพรวม
การสร้างระบบการนำทางที่ปรับเปลี่ยนตามอุปกรณ์เป็นเรื่องยาก ผู้ใช้บางรายจะใช้แป้นพิมพ์ บางรายจะใช้เดสก์ท็อปประสิทธิภาพสูง และบางรายจะเข้าชมจากอุปกรณ์เคลื่อนที่ขนาดเล็ก ผู้เข้าชมทุกคนควรเปิดและปิดเมนูได้
กลยุทธ์สำหรับเว็บ
ในการสำรวจคอมโพเนนต์นี้ ฉันได้ผสมผสานฟีเจอร์สำคัญบางอย่างของแพลตฟอร์มเว็บเข้าด้วยกันอย่างสนุกสนาน
- CSS
:target - ตารางกริด CSS
- การแปลง CSS
- CSS Media Query สำหรับวิวพอร์ตและค่ากำหนดของผู้ใช้
- JS สำหรับการปรับปรุง UX ของ
focus
โซลูชันของฉันมีแถบด้านข้าง 1 แถบและสลับเฉพาะเมื่ออยู่ในวิวพอร์ต "อุปกรณ์เคลื่อนที่" ที่มีขนาด 540px หรือน้อยกว่า
540px จะเป็นเบรกพอยต์สำหรับการสลับระหว่างเลย์เอาต์แบบอินเทอร์แอกทีฟบนอุปกรณ์เคลื่อนที่กับเลย์เอาต์แบบคงที่บนเดสก์ท็อป
คลาสสมมติ :target ของ CSS
ลิงก์ <a> หนึ่งจะตั้งค่าแฮช URL เป็น #sidenav-open และอีกลิงก์หนึ่งจะตั้งค่าเป็นค่าว่าง ('')
สุดท้าย องค์ประกอบจะมี id เพื่อให้ตรงกับแฮช ดังนี้
<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<aside id="sidenav-open">
…
</aside>
การคลิกลิงก์แต่ละรายการเหล่านี้จะเปลี่ยนสถานะแฮชของ URL ของหน้าเว็บ จากนั้นฉันจะแสดงและซ่อนแถบนำทางด้านข้างด้วยคลาสเทียม
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
ตารางกริด CSS
ในอดีต ฉันใช้เฉพาะเลย์เอาต์และคอมโพเนนต์การนำทางด้านข้างแบบตำแหน่งสัมบูรณ์หรือแบบคงที่เท่านั้น แต่ Grid ที่มีไวยากรณ์ grid-area ช่วยให้เรากำหนดองค์ประกอบหลายรายการให้กับแถวหรือคอลัมน์เดียวกันได้
กลุ่ม
องค์ประกอบเลย์เอาต์หลัก #sidenav-container คือตารางกริดที่สร้าง 1 แถวและ 2 คอลัมน์ โดยแต่ละรายการมีชื่อว่า stack เมื่อพื้นที่ถูกจำกัด CSS จะกำหนดองค์ประกอบย่อยทั้งหมดของ <main> ให้กับชื่อตารางกริดเดียวกัน โดยวางองค์ประกอบทั้งหมดไว้ในพื้นที่เดียวกันเพื่อสร้างกองซ้อน
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
ฉากหลังของเมนู
<aside> เป็นองค์ประกอบภาพเคลื่อนไหวที่มีการนำทางด้านข้าง โดยมีองค์ประกอบย่อย 2 รายการ ได้แก่ คอนเทนเนอร์การนำทาง <nav> ชื่อ [nav] และฉากหลัง <a> ชื่อ [escape] ซึ่งใช้เพื่อปิดเมนู
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
ปรับ 2fr และ 1fr เพื่อค้นหาอัตราส่วนที่ต้องการสำหรับการวางซ้อนเมนูและปุ่มปิดพื้นที่ว่างทางลบ
การเปลี่ยนรูปแบบและการเปลี่ยน CSS 3 มิติ
ตอนนี้เลย์เอาต์ของเราซ้อนกันที่ขนาดวิวพอร์ตบนอุปกรณ์เคลื่อนที่ จนกว่าฉันจะเพิ่มสไตล์ใหม่ ระบบจะวางซ้อนบทความของเราโดยค่าเริ่มต้น ต่อไปนี้คือ UX บางส่วนที่เราต้องการนำเสนอในส่วนถัดไป
- ภาพเคลื่อนไหวการเปิดและปิด
- ใช้ภาพเคลื่อนไหวที่มีการเคลื่อนไหวก็ต่อเมื่อผู้ใช้ยอมรับเท่านั้น
- ทำให้
visibilityเคลื่อนไหวเพื่อให้โฟกัสของแป้นพิมพ์ไม่เข้าสู่องค์ประกอบที่อยู่นอกหน้าจอ
เมื่อเริ่มใช้ภาพเคลื่อนไหว ฉันต้องการเริ่มโดยคำนึงถึงการช่วยเหลือพิเศษเป็นอันดับแรก
การเคลื่อนไหวที่เข้าถึงได้
ไม่ใช่ทุกคนที่ต้องการประสบการณ์การเคลื่อนไหวแบบเลื่อนออก ในโซลูชันของเรา ค่ากำหนดนี้จะใช้โดยการปรับตัวแปร CSS --duration ภายในคิวรี่สื่อ ค่าการค้นหาสื่อนี้แสดงถึงค่ากำหนดของระบบปฏิบัติการของผู้ใช้สำหรับการเคลื่อนไหว (หากมี)
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
ตอนนี้เมื่อแถบนำทางด้านข้างเลื่อนเปิดและปิด หากผู้ใช้ต้องการลดการเคลื่อนไหว ฉันจะย้ายองค์ประกอบไปยังมุมมองทันที โดยคงสถานะไว้โดยไม่มีการเคลื่อนไหว
การเปลี่ยนผ่าน การเปลี่ยนรูปแบบ การแปลภาษา
Sidenav out (ค่าเริ่มต้น)
หากต้องการตั้งค่าสถานะเริ่มต้นของแถบนำทางด้านข้างบนอุปกรณ์เคลื่อนที่เป็นสถานะนอกหน้าจอ
ฉันจะวางตำแหน่งองค์ประกอบด้วย transform: translateX(-110vw)
โปรดทราบว่าฉันได้เพิ่ม 10vw อีกรายการลงในโค้ดนอกหน้าจอทั่วไปของ -100vw เพื่อให้แน่ใจว่า box-shadow ของแถบนำทางด้านข้างจะไม่ปรากฏในวิวพอร์ตหลักเมื่อซ่อนอยู่
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
}
}
Sidenav in
เมื่อองค์ประกอบ #sidenav ตรงกับ :target ให้ตั้งค่าตำแหน่ง translateX() เป็นฐานหลัก 0 และดู CSS เลื่อนองค์ประกอบจากตำแหน่ง "ออก" ของ -110vw ไปยังตำแหน่ง "เข้า" ของ 0 ในช่วง var(--duration) เมื่อแฮช URL มีการเปลี่ยนแปลง
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
ระดับการเข้าถึงการเปลี่ยนผ่าน
เป้าหมายตอนนี้คือการซ่อนเมนูจากโปรแกรมอ่านหน้าจอเมื่อเมนูหายไป เพื่อไม่ให้ระบบโฟกัสที่เมนูนอกหน้าจอ ฉันทำได้โดยตั้งค่าการเปลี่ยนระดับการเข้าถึงเมื่อ :target มีการเปลี่ยนแปลง
- เมื่อเข้าไป ไม่ต้องเปลี่ยนระดับการมองเห็น ให้มองเห็นได้ทันทีเพื่อให้ฉันเห็นองค์ประกอบเลื่อนเข้ามาและรับโฟกัส
- เมื่อออก ให้เปลี่ยนระดับการมองเห็นแต่หน่วงเวลาไว้ เพื่อให้เปลี่ยนเป็น
hiddenเมื่อสิ้นสุดการเปลี่ยนออก
การปรับปรุง UX ของการช่วยเหลือพิเศษ
ลิงก์
โซลูชันนี้อาศัยการเปลี่ยน URL เพื่อจัดการสถานะ
โดยปกติแล้ว คุณควรใช้องค์ประกอบ <a> ที่นี่ และจะได้รับฟีเจอร์การช่วยเหลือพิเศษดีๆ บางอย่างโดยไม่มีค่าใช้จ่าย มาตกแต่งองค์ประกอบแบบอินเทอร์แอกทีฟด้วยป้ายกำกับที่สื่อถึงเจตนาอย่างชัดเจนกัน
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
<svg>...</svg>
</a>
ตอนนี้ปุ่มการโต้ตอบหลักของเราจะระบุเจตนาอย่างชัดเจนสำหรับทั้งเมาส์และแป้นพิมพ์
:is(:hover, :focus)
ตัวเลือกเทียมเชิงฟังก์ชัน CSS ที่มีประโยชน์นี้ช่วยให้เราสามารถรวมสไตล์การวางเมาส์เหนือองค์ประกอบได้อย่างรวดเร็วโดยแชร์สไตล์เหล่านั้นกับการโฟกัสด้วย
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
เพิ่ม JavaScript
กด escape เพื่อปิด
แป้น Escape บนแป้นพิมพ์ควรปิดเมนูใช่ไหม มาต่อสายไฟกัน
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
ประวัติการเข้าชมของเบราว์เซอร์
หากต้องการป้องกันไม่ให้การโต้ตอบแบบเปิดและปิดซ้อนรายการหลายรายการในประวัติการท่องเว็บ ให้เพิ่ม JavaScript ต่อไปนี้ในบรรทัดลงในปุ่มปิด
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
การดำเนินการนี้จะนำรายการประวัติ URL ออกเมื่อปิด ทำให้เหมือนกับว่าไม่เคยเปิดเมนูเลย
UX สำหรับการทำงาน
ข้อมูลโค้ดถัดไปจะช่วยให้เราโฟกัสที่ปุ่มเปิดและปิดหลังจากที่เปิดหรือปิด ฉันต้องการทำให้การสลับเป็นเรื่องง่าย
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
เมื่อแถบนำทางด้านข้างเปิดขึ้น ให้โฟกัสที่ปุ่มปิด เมื่อแถบนำทางด้านข้างปิดลง
ให้โฟกัสที่ปุ่มเปิด ฉันทำได้โดยเรียกใช้ focus() ในองค์ประกอบใน JavaScript
บทสรุป
ตอนนี้คุณก็รู้แล้วว่าเราทำยังไง แล้วคุณล่ะจะทำยังไง ซึ่งทำให้สถาปัตยกรรมคอมโพเนนต์น่าสนใจยิ่งขึ้น ใครจะสร้างเวอร์ชันแรกที่มีช่องบ้าง 🙂
มาปรับเปลี่ยนแนวทางและเรียนรู้วิธีทั้งหมดในการสร้างสรรค์บนเว็บกัน สร้าง Glitch ทวีตเวอร์ชันของคุณมาให้ฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- @_developit พร้อมองค์ประกอบที่กำหนดเอง: เดโมและโค้ด
- @mayeedwin1 ด้วย HTML/CSS/JS: เดโมและโค้ด
- @a_nurella พร้อม Glitch Remix: เดโมและโค้ด
- @EvroMalarkey ด้วย HTML/CSS/JS: เดโมและโค้ด