การค้นหาคอนเทนเนอร์สถานะการเลื่อนช่วยให้คุณจัดสไตล์องค์ประกอบตามสถานะการเลื่อนปัจจุบันได้ เช่น องค์ประกอบ "ติด" (ผ่านการจัดตำแหน่งแบบติดหนึบ) หรือ "สแนป" (ผ่านการสแนปการเลื่อน) หรือไม่ ซึ่งจะช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานภาพสไลด์หรือแกลเลอรีที่สามารถแยกแยะรายการที่ใช้งานอยู่ด้วยภาพได้โดยไม่ต้องอาศัย JavaScript Intersection Observer หรือ Listener เหตุการณ์การเลื่อน
การติดตั้งใช้งานหลัก
หากต้องการไฮไลต์รายการที่สแนป คุณต้องสร้างคอนเทนเนอร์สแนปการเลื่อน กำหนดเป้าหมายสแนปเป็นคอนเทนเนอร์สถานะการเลื่อน แล้วค้นหาสถานะนั้นเพื่อจัดสไตล์องค์ประกอบสืบทอด
1. สร้างคอนเทนเนอร์การสแนปการเลื่อน
คอนเทนเนอร์หลักต้องเปิดใช้ scroll-snap-type
<div class="carousel">
<div class="carousel-item">
<div class="card">Product 1 content</div>
</div>
<div class="carousel-item">
<div class="card">Product 2 content</div>
</div>
</div>
.carousel {
display: flex;
overflow-x: auto;
/* MANDATORY: Enable scroll snapping on the container */
scroll-snap-type: x mandatory;
}
2. กำหนดเป้าหมายการสแนปเป็นคอนเทนเนอร์สถานะการเลื่อน
แต่ละรายการในภาพสไลด์ที่ควรติดตามเพื่อจัดชิดต้องประกาศเป็นคอนเทนเนอร์ scroll-state
.carousel-item {
/* Define where the item snaps within the container */
scroll-snap-align: center;
/* MANDATORY: Establish this element as a scroll-state query container */
container-type: scroll-state;
}
3. ค้นหาสถานะ snapped
เนื่องจากการค้นหาคอนเทนเนอร์จะจัดรูปแบบองค์ประกอบสืบทอด คุณจึงต้องใช้รูปแบบไฮไลต์กับองค์ประกอบภายในเป้าหมายสแนป เนื่องจากคอนเทนเนอร์การเลื่อนตั้งค่าเป็นโอเวอร์โฟลว์ในแกน x ให้ใช้การค้นหา scroll-state(snapped: x)
บังคับ: รวมสไตล์ไว้ใน @media (prefers-reduced-motion: no-preference) เพื่อแสดงเอฟเฟกต์ต่อผู้ใช้ที่ไม่ได้ขอให้ลดการเคลื่อนไหวเท่านั้น คุณอาจคงบางส่วนของเอฟเฟกต์ไว้ได้ ทั้งนี้ขึ้นอยู่กับกรณีการใช้งาน แต่ในกรณีนี้ การ์ดจะกะพริบจากสีขาวเป็นสีน้ำเงินในลักษณะที่อาจทำให้ผู้ใช้บางรายเกิดปัญหา เราจึงปิดใช้เอฟเฟกต์นี้โดยสมบูรณ์
/* Specify transition outside of queries so that it is applied regardless of state. */
.card {
transition:
scale 0.4s cubic-bezier(0.25, 0.8, 0.25, 1),
background-color 0.4s,
color 0.4s,
box-shadow 0.4s;
}
/*
Only show the effect for users not requesting reduced motion. Disable completely, including the color change, as it causes a flash that may be problematic.
*/
@media (prefers-reduced-motion: no-preference) {
/* Style the content when its parent .carousel-item is snapped on the x axis */
@container scroll-state(snapped: x) {
.card {
background: #007bff;
color: white;
scale: 1.15;
box-shadow: 0 10px 25px rgba(0, 123, 255, 0.3);
}
}
}
/* MANDATORY Copy-Paste Safety: Disable highlight scaling/flashing for motion sensitive users */
@media (prefers-reduced-motion: reduce) {
.card {
transition: none !important;
scale: 1 !important;
}
}
ข้อบ่งชี้ snapped สามารถค้นหาแกนที่เฉพาะเจาะจงได้ ได้แก่ x, y, inline, block หรือ both
การช่วยเหลือพิเศษ
AVOID: การใช้ scroll-state กับองค์ประกอบแบบอินเทอร์แอกทีฟ
ไฮไลต์ภาพสำหรับรายการที่สแนปสามารถปรับปรุง UX ได้ แต่รายการที่สแนปจะไม่แสดงต่อต้นไม้การช่วยเหลือพิเศษ ธีมภาพที่ใช้กับรายการที่สแนปไม่ควรสื่อว่าองค์ประกอบนั้นทำงานอยู่หรือโฟกัสอยู่ และวงแหวนโฟกัสของแป้นพิมพ์ควรเห็นได้ชัดเจนและแตกต่างจากไฮไลต์ snapped หากรายการที่สแนปเป็นแบบอินเทอร์แอกทีฟ คุณต้องใช้แนวทางปฏิบัติด้านการช่วยเหลือพิเศษมาตรฐานอื่นๆ เพื่อให้เข้าถึงได้
การสแนปเกิดขึ้นเนื่องจากการเลื่อน ซึ่งไม่ได้ย้ายโฟกัสของแป้นพิมพ์ อย่างไรก็ตาม โฟกัสของแป้นพิมพ์อาจทำให้คอนเทนเนอร์การเลื่อนเคลื่อนที่ ซึ่งส่งผลให้เกิดการเปลี่ยนแปลงในรายการที่สแนป ซึ่งอาจเป็นหรือไม่เป็นรายการที่โฟกัสก็ได้ ซึ่งอาจทำให้ผู้ใช้สับสนและเราไม่แนะนำให้ทำเช่นนี้
กลยุทธ์สำรอง
สำหรับเบราว์เซอร์ที่ไม่รองรับการค้นหาสถานะการเลื่อน คุณควรให้ประสบการณ์การใช้งานพื้นฐานที่ใช้งานได้ซึ่งรายการทั้งหมดอ่านได้ แม้ว่าจะไม่มีไฮไลต์ "ใช้งานอยู่" ก็ตาม
การตรวจจับฟีเจอร์
คุณสามารถใช้ @supports เพื่อปรับปรุงเฉพาะเบราว์เซอร์ที่รองรับได้ดังนี้
@supports (container-type: scroll-state) {
/* Enhancement styles here */
}
JavaScript สำรอง
หากไฮไลต์มีความสำคัญต่อประสบการณ์ของผู้ใช้ ให้ใช้ IntersectionObserver เพื่อระบุรายการที่สแนป ปรับพื้นที่ที่สังเกตได้ให้เป็นส่วนบางๆ ตรงกลางภาพสไลด์โดยระบุ rootMargin ที่มีค่าอินไลน์เป็นลบ เช่น หากต้องการพิจารณาว่าองค์ประกอบหนึ่งๆ มีการตัดกันหากอยู่ในส่วนกลาง 2% ของภาพสไลด์ ให้ตั้งค่า rootMargin เป็น "0px -49%"
// Optional: detect support and apply a JS-based fallback
if (!CSS.supports('container-type', 'scroll-state')) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
// Toggle a class based on intersection
entry.target.classList.toggle('is-snapped', entry.isIntersecting);
});
}, {
root: document.querySelector(".carousel"),
// Carousel item intersects if any part of the carousel item is in the middle 2% of the carousel.
rootMargin: "0px -49%"
});
document.querySelectorAll('.carousel-item').forEach(item => {
observer.observe(item);
});
}