ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างแถบด้านข้างแบบเลื่อนออกที่ปรับเปลี่ยนตามอุปกรณ์
ในโพสต์นี้ ฉันต้องการแชร์วิธีสร้างต้นแบบคอมโพเนนต์แถบด้านข้างสำหรับเว็บที่ตอบสนองต่อผู้ใช้ เก็บสถานะ รองรับการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ ทำงานได้ทั้งในและนอก JavaScript และทำงานได้กับเบราว์เซอร์ต่างๆ ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์เวอร์ชัน YouTube ที่นี่
ภาพรวม
การสร้างระบบการนําทางที่ตอบสนองต่อผู้ใช้นั้นเป็นเรื่องยาก ผู้ใช้บางรายจะใช้แป้นพิมพ์ บางรายจะมีเดสก์ท็อปที่มีประสิทธิภาพ และบางรายจะเข้าชมจากอุปกรณ์เคลื่อนที่ขนาดเล็ก ทุกคนที่เข้าชมควรเปิดและปิดเมนูได้
กลยุทธ์บนเว็บ
ในการสำรวจคอมโพเนนต์นี้ เรามีความสุขที่ได้รวมฟีเจอร์สําคัญของแพลตฟอร์มเว็บเข้าด้วยกัน ดังนี้
- CSS
:target
- ตารางกริด CSS
- การแปลง CSS
- คิวรีสื่อ CSS สำหรับวิวพอร์ตและค่ากําหนดของผู้ใช้
- 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 ของหน้าเว็บ จากนั้นฉันจะแสดงและซ่อนแถบด้านข้างด้วยพร็อกซีคลาส ดังนี้
@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
เพื่อหาสัดส่วนที่ต้องการสำหรับเมนูที่วางซ้อนและปุ่มปิดของพื้นที่เชิงลบ
การเปลี่ยนรูปแบบและการเปลี่ยน 3 มิติของ CSS
ตอนนี้เลย์เอาต์ของเราซ้อนกันอยู่ที่ขนาดวิวพอร์ตของอุปกรณ์เคลื่อนที่ ในระหว่างนี้ รูปแบบจะวางซ้อนกับบทความโดยค่าเริ่มต้น UX ที่เราต้องการในส่วนถัดไปมีดังนี้
- ภาพเคลื่อนไหวการเปิดและปิด
- ใช้ภาพเคลื่อนไหวที่มีการเคลื่อนไหวเฉพาะในกรณีที่ผู้ใช้อนุญาต
- สร้างการเคลื่อนไหวของ
visibility
เพื่อไม่ให้โฟกัสของแป้นพิมพ์เข้าสู่องค์ประกอบที่อยู่นอกหน้าจอ
เมื่อเริ่มใช้ภาพเคลื่อนไหว เราต้องการเริ่มต้นด้วยการคำนึงถึงการช่วยเหลือพิเศษสำหรับผู้ทุพพลภาพเป็นอันดับแรก
การเคลื่อนไหวที่เข้าถึงได้
ไม่ใช่ทุกคนที่ต้องการประสบการณ์การเคลื่อนไหวแบบเลื่อนออก ในโซลูชันของเรา ค่ากําหนดนี้จะมีผลโดยการปรับตัวแปร CSS --duration
ภายใน Media Query ค่าการค้นหาสื่อนี้แสดงถึงค่ากําหนดของระบบปฏิบัติการของผู้ใช้สําหรับการเคลื่อนไหว (หากมี)
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
ตอนนี้เมื่อแถบด้านข้างเลื่อนเปิดและปิด หากผู้ใช้ต้องการลดการเคลื่อนไหว เราจะย้ายองค์ประกอบไปยังมุมมองทันทีโดยคงสถานะไว้โดยไม่มีการเคลื่อนไหว
การเปลี่ยนฉาก การเปลี่ยนรูปแบบ การเปลี่ยนภาษา
แถบด้านข้างออก (ค่าเริ่มต้น)
หากต้องการตั้งค่าสถานะเริ่มต้นของแถบด้านข้างในอุปกรณ์เคลื่อนที่เป็นสถานะ "นอกหน้าจอ" ฉันจะจัดตําแหน่งองค์ประกอบด้วย 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
ตรงกับ :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 ออกเมื่อปิด ราวกับว่าไม่เคยเปิดเมนู
Focus 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
บทสรุป
ตอนนี้คุณรู้วิธีที่เราทําแล้ว คุณจะทําอย่างไร โครงสร้างคอมโพเนนต์นี้จึงมีความสนุก Who's going to make the 1st version with slots? 🙂
มาลองใช้แนวทางที่หลากหลายและดูวิธีทั้งหมดในการสร้างบนเว็บกัน สร้างGlitch แล้วทวีตเวอร์ชันของคุณมาให้เรา เราจะเพิ่มเวอร์ชันของคุณลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
- @_developit พร้อมองค์ประกอบที่กำหนดเอง: demo และโค้ด
- @mayeedwin1 สำหรับ HTML/CSS/JS: เดโมและโค้ด
- @a_nurella กับรีมิกซ์แบบ Glitch: เดโมและโค้ด
- @EvroMalarkey สำหรับ HTML/CSS/JS: เดโมและโค้ด