Zapytania o kontenery stanu przewijania umożliwiają stylizowanie elementów na podstawie ich bieżącego stanu przewijania, np. tego, czy element jest „przyklejony” (za pomocą pozycjonowania przyklejonego) czy „przyciągnięty” (za pomocą przewijania z przyciąganiem). Umożliwia to wyświetlanie karuzeli lub galerii, w których aktywny element można wizualnie odróżnić bez polegania na obserwatorach przecięcia JavaScriptu lub detektorach zdarzeń przewijania.
Implementacja podstawowa
Aby wyróżnić przyciągane elementy, musisz utworzyć kontener przewijania z przyciąganiem, zdefiniować cele przyciągania jako kontenery stanu przewijania, a następnie wysłać zapytanie o ten stan, aby nadać styl elementom podrzędnym.
1. Tworzenie kontenera przewijania z przyciąganiem
Kontener nadrzędny musi mieć włączoną opcję 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. Definiowanie elementów docelowych przyciągania jako kontenerów stanu przewijania
Każdy element w karuzeli, który ma być śledzony pod kątem przyciągania, musi być zadeklarowany jako kontener 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. Sprawdź stan: snapped
Zapytania o kontener określają styl elementów podrzędnych, dlatego musisz zastosować style wyróżnienia do elementu wewnątrz celu przyciągania. Ponieważ kontener przewijania jest ustawiony na przepełnienie na osi X, użyj zapytania scroll-state(snapped: x).
OBOWIĄZKOWO: umieść style w @media (prefers-reduced-motion: no-preference), aby efekt był widoczny tylko dla użytkowników, którzy nie poprosili o ograniczenie ruchu. W zależności od przypadku użycia możesz zachować części efektu, ale w tym przypadku karty migają od białego do niebieskiego w sposób, który może powodować problemy u niektórych użytkowników, dlatego całkowicie go wyłączamy.
/* 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;
}
}
Deskryptor snapped może wysyłać zapytania do konkretnych osi: x, y, inline, block lub both.
Ułatwienia dostępu
AVOID używania scroll-state z elementami interaktywnymi.
Wizualne wyróżnienie przyciągniętych elementów może poprawić wrażenia użytkownika, ale przyciągnięty element nie jest widoczny w drzewie ułatwień dostępu. Motyw wizualny zastosowany do przyciągniętego elementu nie powinien sugerować, że element jest aktywny lub zaznaczony, a pierścień zaznaczenia klawiatury powinien być dobrze widoczny i odróżniać się od wyróżnienia snapped. Jeśli przyciągnięty element jest interaktywny, musisz zastosować inne standardowe praktyki dotyczące ułatwień dostępu, aby był on dostępny.
Przyciąganie następuje podczas przewijania, które nie powoduje przeniesienia zaznaczenia klawiatury. Zaznaczenie za pomocą klawiatury może jednak spowodować przesunięcie kontenera z paskiem przewijania, co może spowodować zmianę przyciągniętego elementu, który może, ale nie musi być zaznaczonym elementem. Może to dezorientować użytkowników, dlatego nie zalecamy takiego rozwiązania.
Strategie zastępcze
W przypadku przeglądarek, które nie obsługują zapytań o stan przewijania, należy zapewnić funkcjonalne podstawowe działanie, w którym wszystkie elementy są czytelne, nawet bez „aktywnego” wyróżnienia.
Wykrywanie funkcji
Możesz użyć @supports, aby udostępniać ulepszenia tylko obsługiwanym przeglądarkom:
@supports (container-type: scroll-state) {
/* Enhancement styles here */
}
Zastępczy kod JavaScript
Jeśli wyróżnienie jest kluczowe dla wrażeń użytkownika, użyj IntersectionObserver, aby określić przyciągnięty element. Dostosuj obserwowany obszar do wąskiego wycinka w środku karuzeli, podając rootMargin z ujemną wartością w kodzie. Jeśli na przykład chcesz, aby element był uznawany za przecinający się, gdy znajduje się w środkowych 2% karuzeli, ustaw rootMargin na "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);
});
}