Wzorce przesuwania w celu wyświetlenia umożliwiają użytkownikom przesuwanie wiersza lub panelu treści na pierwszym planie w poziomie, aby odsłonić ukryty interfejs, np. przyciski czynności kontekstowych (Przypnij, Wycisz, Archiwizuj, Usuń) lub dodatkowe menu boczne, bez automatycznego aktywowania żadnej czynności. Zamiast wykonywać działanie podczas przesuwania, kontener otwiera się, aby wyświetlić interaktywne elementy sterujące, które użytkownik może sprawdzić i aktywować lub zamknąć, przesuwając palcem z powrotem.
Aby dowiedzieć się, jak używać gestu przesunięcia na pełną szerokość, który natychmiast zatwierdza i odrzuca element po przekroczeniu progu, zobacz Przesuń, aby usunąć. Informacje o wyświetlaniu ukrytego nagłówka u góry kontenera przewijania pionowego znajdziesz w sekcji Przeciągnij, aby wyświetlić.
Jak to zrobić?
Niezależnie od tego, czy chcesz wyświetlić przyciski poleceń na elemencie listy, czy menu boczne obok przewijanego panelu, mechanizm jest taki sam: umieść zawartość pierwszego planu i ukryty panel w zewnętrznym kontenerze przewijania poziomego skonfigurowanym za pomocą przewijania z przyciąganiem CSS (scroll-snap-type: x mandatory) i overscroll-behavior: chain. Natywny mechanizm przewijania przeglądarki obsługuje pęd, przyciąganie, przewijanie z fokusem klawiatury i łańcuch przewijania bez detektorów gestów JavaScriptu.
Krok 1. Oznacz element swipe wrapper, treść i wyświetlany panel
Umieść najpierw treść na pierwszym planie, a następnie elementy sterujące (przyciski poleceń lub menu boczne) w kontenerze przewijania .SwipeReveal. Ponieważ treść jest pierwsza w kolejności DOM, kontener przewijania naturalnie zaczyna się od przesunięcia przewijania 0, a panel jest ukryty poza ekranem po prawej stronie.
<!-- Example A: List item with revealable action buttons -->
<div class="SwipeReveal">
<div class="SwipeReveal-content">
<strong>Design review notes</strong>
<p>Updated component specs for the Q3 release.</p>
</div>
<div class="SwipeReveal-panel" role="group" aria-label="Item actions">
<button type="button">Pin</button>
<button type="button">Archive</button>
<button type="button">Delete</button>
</div>
</div>
<!-- Example B: Scrollable content pane with a revealable side menu -->
<div class="SwipeReveal" role="region" aria-label="Document preview with swipeable side menu">
<div class="SwipeReveal-content is-scrollable" tabindex="0">
<h2>Article Preview</h2>
<p>Scroll vertically through this pane, or swipe left to reveal the side menu...</p>
</div>
<nav class="SwipeReveal-panel" aria-label="Contextual menu">
<a href="#share">Share</a>
<a href="#bookmark">Bookmark</a>
<a href="#export">Export PDF</a>
</nav>
</div>
Krok 2. Skonfiguruj poziomy kontener przyciągania i overscroll-behavior: chain
Gdy opakujesz element w zewnętrzny kontener przewijania wyłącznie po to, aby utworzyć efekt przesunięcia, ten zewnętrzny kontener stanie się pośrednim elementem przewijania w dokumencie (lub innym nadrzędnym kontenerze przewijania).
Cztery słowa kluczowe overscroll-behavior niezależnie kontrolują, czy nadmierne przewijanie jest przekazywane do nadrzędnego kontenera przewijania (łańcuch przewijania) i czy kontener wyświetla lokalne elementy nadmiernego przewijania, takie jak elastyczne rozciąganie lub poświata krawędzi (lokalny efekt graniczny):
| Wartość | Łączenie przewijania (propagacja do elementu nadrzędnego) | Efekt granicy lokalnej (gumka recepturka / poświata) | Rola w reklamie typu przesuń, aby odkryć |
|---|---|---|---|
auto |
Tak | Tak | Działanie domyślne. Pozostaw tę opcję włączoną w przypadku wewnętrznych elementów przewijania treści, gdzie pożądane jest normalne odbicie, ale unikaj jej w przypadku zewnętrznego elementu swipe, gdzie powoduje odbicie menu. |
chain |
Tak | Nie | Użyj na zewnętrznym elemencie przewijania .SwipeReveal, aby nadmiarowe przewijanie było przekazywane do nadrzędnego elementu przewijania (np. dokumentu) bez odbijania menu lub paska działań |
contain |
Nie | Tak | Przechwytuje przewijanie wewnątrz kontenera, jednocześnie odbijając się lokalnie |
none |
Nie | Nie | Blokuje zarówno lokalne odbicie, jak i łańcuch przewijania do elementów nadrzędnych. |
Zmień .SwipeReveal na siatkę dwukolumnową (grid-template-columns: 100% max-content), w której .SwipeReveal-content przyciąga do start, a .SwipeReveal-panel przyciąga do end:
.SwipeReveal {
/* Column 1: full-width content. Column 2: auto-sized actions or side menu. */
display: grid;
grid-template-columns: 100% max-content;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
/* Prevent the swipe wrapper and revealed panel from bouncing locally while
still chaining excess scroll into the parent scroller (the document). */
overscroll-behavior: chain;
}
@media (prefers-reduced-motion: no-preference) {
.SwipeReveal {
scroll-behavior: smooth;
}
}
.SwipeReveal-content {
scroll-snap-align: start;
scroll-initial-target: nearest;
background: Canvas;
}
.SwipeReveal-content:focus-visible {
outline: auto;
outline-offset: -2px;
}
.SwipeReveal-content.is-scrollable {
block-size: 20rem;
overflow: auto;
/* Keeps default `overscroll-behavior: auto` so the content pane retains
its normal bounce effect while chaining horizontal swipes to `.SwipeReveal`. */
}
.SwipeReveal-panel {
/* Snapping a trailing panel to `end` (or a leading panel to `start`) brings
all controls fully into view while keeping the adjacent edge of the content visible. */
scroll-snap-align: end;
display: flex;
&:first-child {
scroll-snap-align: start;
}
}
Jeśli chcesz też wyświetlać elementy sterujące po lewej stronie (
inline-start) w 3 kolumnach (grid-template-columns: max-content 100% max-content) lub 2 kolumnach (max-content 100%), umieść element.SwipeReveal-panelprzed elementem.SwipeReveal-content. Ponieważ.SwipeReveal-contentmascroll-initial-target: nearest, kontener przewijania początkowo spoczywa na kolumnie treści, a nie na panelu głównym.
Krok 3. Przywracanie widoczności paneli treści, które można zaznaczyć, po zaznaczeniu ich za pomocą klawiatury
Ponieważ przyciski lub linki w elemencie .SwipeReveal-panel są domyślnie poza ekranem, przechodzenie do nich za pomocą klawisza Tab powoduje automatyczne przewinięcie elementu .SwipeReveal, aby wyświetlić zaznaczony element sterujący. Jednak ponieważ element .SwipeReveal-content zajmuje 100% szerokości kontenera zewnętrznego, większość z niego pozostaje wewnątrz obszaru przewijania nawet wtedy, gdy panel boczny jest otwarty. Dlatego naciśnięcie klawisza Shift+Tab w celu przeniesienia fokusu z powrotem na element .SwipeReveal-content (lub na element wewnątrz niego, który może być zaznaczony) nie spowoduje automatycznego przewinięcia ścieżki z powrotem do elementu 0.
Nasłuchuj zdarzenia focusin na .SwipeReveal-content i sprawdź event.target.matches(':focus-visible'), aby zaznaczenie klawiaturą panelu lub dowolnego elementu podrzędnego wracało do .SwipeReveal-content bez zakłócania kliknięć wskaźnikiem lub przeciągnięć dotykiem:
for (const content of document.querySelectorAll('.SwipeReveal-content')) {
content.addEventListener('focusin', (event) => {
if (event.target.matches(':focus-visible')) {
content.scrollIntoView({
block: 'nearest',
inline: 'start',
});
}
});
}
Sprawdzone metody i pułapki
- DO use
scroll-snap-type: x mandatoryrather thanproximity. W przypadkuproximitylekkie przesunięcie może spowodować, że przyciski poleceń lub menu boczne pozostaną częściowo widoczne. - USTAW
overscroll-behavior: chainna zewnętrznym poziomym elemencie przewijania, który można odsłonić, przesuwając palcem (.SwipeReveal), aby przewijanie mogło zostać przeniesione do nadrzędnego elementu przewijania bez odbijania odsłoniętego menu lub paska działań. - USTAW wartość
overscroll-behavior: auto(domyślną) w przypadku wewnętrznych paneli z treścią przewijaną (.SwipeReveal-content.is-scrollable), w których chcesz, aby występowało normalne odbicie, a jednocześnie chcesz połączyć przesunięcia poziome z zewnętrznym elementem przewijania. - Zaimplementuj widoczne działania i elementy menu bocznego jako rzeczywiste elementy sterujące DOM, które można zaznaczyć (
<button>,<a>). Gdy element.SwipeReveal-contentmożna zaznaczyć lub zawiera on elementy sterujące, które można zaznaczyć, połącz detektorfocusinz elementemevent.target.matches(':focus-visible'), aby wywołaćscrollIntoView({ block: 'nearest', inline: 'start' }). Dzięki temu powrót do panelu treści za pomocą klawisza Tab spowoduje zamknięcie panelu bocznego bez wpływu na interakcje ze wskaźnikiem. - NIE przechwytuj zdarzeń
wheel,touchmoveanipointermovew kodzie JavaScript, aby ręcznie tłumaczyć treści lub emulować łańcuch przewijania. Przechwytywanie gestów w głównym wątku blokuje przewijanie sterowane przez kompozytor, zakłóca natywne przewijanie i pogarsza czas reakcji.
Funkcja zastępcza
Gdy kontener .SwipeReveal umieszcza element .SwipeReveal-panel po lewej stronie (inline-start) przed elementem .SwipeReveal-content, przeglądarki, które nie obsługują scroll-initial-target: nearest, początkowo renderują stronę z przesunięciem przewijania 0 i wyświetlonym panelem głównym.
Jeśli Twój cel Baseline nie obsługuje scroll-initial-target, wykryj obsługę za pomocą CSS.supports('scroll-initial-target', 'nearest') i wywołaj scrollTo() z behavior: 'instant' na load, aby kontenery z panelem głównym przechodziły bezpośrednio do .SwipeReveal-content bez przewijania strony:
Progresywne ulepszanie
Traktuj overscroll-behavior: chain i scrollbar-width: none jako progresywne ulepszenia bez kreacji zastępczych JavaScriptu:
- Automatyczna kaskadowa kreacja zastępcza CSS: wartość początkowa właściwości
overscroll-behaviortoauto. Przeglądarki, które nie rozpoznają jeszcze słowa kluczowegochain, ignorująoverscroll-behavior: chainpodczas analizowania i zachowująauto.autojuż zezwala na łańcuchowe przewijanie do nadrzędnych kontenerów przewijania, więc zarówno gest przesunięcia, jak i łańcuchowe przewijanie do elementu nadrzędnego działają we wszystkich przeglądarkach bez dodatkowej konfiguracji. Przeglądarki, które obsługują tę funkcję, po prostu tłumią niepożądany efekt odbicia na zewnętrznym elemencie opakowującym. - Zastępowanie
containlubnone: jeśli podstawowy arkusz stylów lub klasa narzędziowa ustawiaoverscroll-behavior: containlubnone, zadeklarujoverscroll-behavior: autobezpośrednio przedoverscroll-behavior: chain, aby przeglądarki, które nie obsługują jeszczechain, nadal łączyły przewijanie z elementem nadrzędnym, zamiast je blokować. - Ukrywanie paska przewijania (
scrollbar-width: none): ukrywanie poziomego paska przewijania ma charakter czysto kosmetyczny. Jeśli starszy silnik ignorujescrollbar-width: none, kontener z funkcją przesuwania w celu wyświetlenia pozostaje w pełni funkcjonalny ze standardowym paskiem przewijania.