ภาพรวมพื้นฐานของวิธีสร้างแถบนำทางด้านข้างแบบเลื่อนออกที่ปรับเปลี่ยนตามอุปกรณ์
ในโพสต์นี้ ผมอยากจะแชร์วิธีสร้างต้นแบบคอมโพเนนต์แถบนำทางด้านข้างสำหรับเว็บที่ ตอบสนองตามอุปกรณ์ต่างๆ มีการจัดการสถานะ รองรับการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ ทำงานได้ทั้งแบบมีและไม่มี JavaScript และทำงานได้ในทุกเบราว์เซอร์ ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์นี้ใน YouTube
ภาพรวม
การสร้างระบบนำทางที่ตอบสนองเป็นเรื่องยาก ผู้ใช้บางรายจะใช้คีย์บอร์ด บางรายจะใช้เดสก์ท็อปที่มีประสิทธิภาพ และบางรายจะเข้าชมจากอุปกรณ์เคลื่อนที่ขนาดเล็ก ผู้เข้าชมทุกคนควรเปิดและปิดเมนูได้
กลยุทธ์บนเว็บ
ในการสำรวจคอมโพเนนต์นี้ ฉันได้รวมฟีเจอร์ที่สำคัญบางอย่างของแพลตฟอร์มเว็บเข้าด้วยกัน
- CSS
:target
- กริด CSS
- การเปลี่ยนรูปแบบ CSS
- CSS Media Queries สำหรับวิวพอร์ตและค่ากำหนดของผู้ใช้
- JS สำหรับ
focus
การปรับปรุง UX
โซลูชันของฉันมีแถบด้านข้าง 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 หน้าเว็บ จากนั้นฉันจะใช้ Pseudo-Class เพื่อแสดงและซ่อนแถบนำทางด้านข้าง
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
CSS Grid
ก่อนหน้านี้ฉันใช้เฉพาะเลย์เอาต์และคอมโพเนนต์แถบนำทางด้านข้างที่มีตำแหน่งแบบสัมบูรณ์หรือแบบคงที่
อย่างไรก็ตาม 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
เพื่อหารูปแบบที่คุณต้องการสำหรับภาพซ้อนทับเมนูและปุ่มปิดพื้นที่ว่าง
การเปลี่ยนรูปแบบและการเปลี่ยน 3 มิติ CSS
ตอนนี้เลย์เอาต์ของเราซ้อนกันที่ขนาดวิวพอร์ตสำหรับอุปกรณ์เคลื่อนที่ จนกว่าฉันจะเพิ่มสไตล์ใหม่ มันจะซ้อนทับบทความของเราโดยค่าเริ่มต้น ต่อไปนี้คือ UX ที่ฉันต้องการในส่วนถัดไป
- เปิดและปิดภาพเคลื่อนไหว
- ใช้ภาพเคลื่อนไหวเฉพาะในกรณีที่ผู้ใช้ตกลงเท่านั้น
- สร้างภาพเคลื่อนไหว
visibility
เพื่อไม่ให้โฟกัสของแป้นพิมพ์เข้าสู่องค์ประกอบนอกหน้าจอ
เมื่อเริ่มใช้ภาพเคลื่อนไหว ฉันต้องการเริ่มต้นโดยคำนึงถึงการช่วยเหลือพิเศษเป็นอันดับแรก
การเคลื่อนไหวที่เข้าถึงได้
ไม่ใช่ทุกคนที่จะต้องการประสบการณ์การเคลื่อนไหวแบบสไลด์ออก ในโซลูชันของเรา ค่ากำหนดนี้
จะใช้โดยการปรับ--duration
ตัวแปร CSS ภายใน Media Query ค่า Media Query นี้แสดงถึง
ค่ากำหนดของระบบปฏิบัติการของผู้ใช้สำหรับการเคลื่อนไหว (หากมี)
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
ตอนนี้เมื่อแถบนำทางด้านข้างเปิดและปิด หากผู้ใช้ต้องการลดการเคลื่อนไหว ฉันจะย้ายองค์ประกอบไปยังมุมมองทันที โดยรักษาสถานะไว้โดยไม่มีการเคลื่อนไหว
เปลี่ยน แปลง แปล
แถบนำทางด้านข้างออก (ค่าเริ่มต้น)
หากต้องการตั้งค่าสถานะเริ่มต้นของแถบนำทางด้านข้างในอุปกรณ์เคลื่อนที่เป็นสถานะนอกหน้าจอ
ฉันจะวางองค์ประกอบด้วย transform: translateX(-110vw)
โปรดทราบว่าฉันได้เพิ่ม 10vw
อีกรายการลงในโค้ดนอกหน้าจอทั่วไปของ -100vw
เพื่อให้แน่ใจว่า box-shadow
ของแถบนำทางด้านข้างจะไม่โผล่เข้ามาใน Viewport หลักเมื่อซ่อนอยู่
@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 ที่มี Custom Elements: การสาธิตและโค้ด
- @mayeedwin1 ด้วย HTML/CSS/JS: เดโมและโค้ด
- @a_nurella กับรีมิกซ์ภาพแตก: สาธิตและโค้ด
- @EvroMalarkey ด้วย HTML/CSS/JS: การสาธิตและโค้ด