Met patronen voor swipen om te onthullen kunnen gebruikers een rij of contentvenster op de voorgrond horizontaal verschuiven om verborgen UI te tonen, zoals contextuele actieknoppen (Vastzetten, Dempen, Archiveren, Verwijderen) of een secundair zijmenu, zonder automatisch een actie te activeren. In plaats van een actie uit te voeren tijdens het swipen, wordt de container geopend om interactieve bedieningselementen te tonen die de gebruiker kan inspecteren en activeren, of terugswipen om te sluiten.
Ga naar Swipen om te verwijderen voor een swipegebaar over de volledige breedte waarmee een item onmiddellijk wordt vastgelegd en gesloten nadat een drempelwaarde is overschreden. Ga naar Pull to Reveal voor meer informatie over hoe je een verborgen kop bovenaan een verticale scrollcontainer onthult.
Hoe wordt dit geïmplementeerd
Of je nu actieknoppen onthult op een lijstitem of een zijmenu naast een scrollbaar deelvenster, de mechanismen zijn hetzelfde: verpak de content op de voorgrond en het verborgen deelvenster in een buitenste horizontale scrollcontainer die is geconfigureerd met CSS Scroll Snap (scroll-snap-type: x mandatory) en overscroll-behavior: chain. De systeemeigen scroll-engine van de browser verwerkt momentum, vastzetten, scrollen met toetsenbordfocus en scrollketens zonder JavaScript-gebaarlisteners.
Stap 1: Markeer de swipe-wrapper, content en het onthulde deelvenster
Plaats de content op de voorgrond eerst en de onthulde bedieningselementen (actieknoppen of een zijmenu) daarna in de scrollcontainer .SwipeReveal. Omdat de content als eerste in de DOM-volgorde staat, begint de scrollcontainer natuurlijk bij scroll-offset 0, waarbij het deelvenster rechts buiten het scherm is verborgen.
<!-- 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>
Stap 2: Stel de horizontale snapcontainer en overscroll-behavior: chain in
Als je een element in een buitenste scrollcontainer plaatst, alleen om een swipe-to-reveal-effect te maken, wordt die buitenste wrapper een tussenliggende scroller in het document (of een andere bovenliggende scrollcontainer).
De 4 zoekwoorden van overscroll-behavior bepalen onafhankelijk van elkaar of overmatig scrollen wordt doorgegeven aan een bovenliggende scrollcontainer (scroll chaining) en of de container lokale overscroll-mogelijkheden toont, zoals elastisch uitrekken of een gloed aan de rand (lokaal grenseffect):
| Waarde | Scrollketen (wordt doorgegeven aan bovenliggend element) | Lokaal grenseffect (elastiek/gloed) | Rol in Swipe-to-Reveal |
|---|---|---|---|
auto |
Ja | Ja | Standaardgedrag; houd dit aan voor scrollers voor interne content waar normale bouncefeedback gewenst is, maar vermijd dit voor de buitenste swipe-wrapper waar het menu stuitert |
chain |
Ja | Nee | Gebruik dit op de buitenste .SwipeReveal-scroller, zodat overmatig scrollen wordt doorgegeven aan de bovenliggende scroller (zoals het document) zonder dat het menu of de actiestrip stuitert |
contain |
Nee | Ja | Vangt scrollen binnen de container op terwijl er lokaal nog steeds wordt gestuiterd |
none |
Nee | Nee | Blokkeert zowel lokaal stuiteren als scrollketens naar bovenliggende elementen |
Maak van .SwipeReveal een raster met 2 kolommen (grid-template-columns: 100% max-content) waarbij .SwipeReveal-content wordt uitgelijnd op start en .SwipeReveal-panel wordt uitgelijnd op 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;
}
}
Als je ook bedieningselementen aan de linkerkant (
inline-start) wilt tonen met 3 kolommen (grid-template-columns: max-content 100% max-content) of 2 kolommen (max-content 100%), plaats je de leidende.SwipeReveal-panelvóór.SwipeReveal-content. Omdat.SwipeReveal-contentscroll-initial-target: nearestheeft, rust de scrollcontainer in eerste instantie op de contentkolom in plaats van op het voorste deelvenster.
Stap 3: Zet focusbare contentvensters weer in beeld bij toetsenbordfocus
Omdat de knoppen of links in .SwipeReveal-panel in rust buiten het scherm vallen, wordt .SwipeReveal automatisch gescrold als je er met een toetsenbord naartoe gaat om de gefocuste bedieningselementen te tonen. Omdat .SwipeReveal-content 100% van de breedte van de buitenste container beslaat, blijft het grootste deel ervan binnen de scrollport, zelfs als het zijvenster is geopend. Als je op Shift+Tab drukt om de focus terug te zetten op .SwipeReveal-content (of op een focusbaar element daarin), wordt de track niet automatisch teruggescrold naar 0.
Luister naar focusin op .SwipeReveal-content en check event.target.matches(':focus-visible') zodat de toetsenbordfocus op het deelvenster of een onderliggend element terugspringt naar .SwipeReveal-content zonder aanwijzerklikken of slepen met aanraken te verstoren:
for (const content of document.querySelectorAll('.SwipeReveal-content')) {
content.addEventListener('focusin', (event) => {
if (event.target.matches(':focus-visible')) {
content.scrollIntoView({
block: 'nearest',
inline: 'start',
});
}
});
}
Best practices en valkuilen
- WEL: Gebruik
scroll-snap-type: x mandatoryin plaats vanproximity. Metproximitykan een lichte swipe de actieknoppen of het zijmenu gedeeltelijk zichtbaar laten in de ruststand. - WEL: Stel
overscroll-behavior: chainin op de buitenste horizontale scroller voor swipen om te onthullen (.SwipeReveal) zodat scrollen kan worden gekoppeld aan de bovenliggende scroller zonder dat het onthulde menu of de actiestrip stuitert. - Laat
overscroll-behavior: auto(de standaardwaarde) WEL staan in deelvensters met scrollbare content (.SwipeReveal-content.is-scrollable) waar je normale bouncefeedback wilt, terwijl je horizontale swipes nog steeds koppelt aan de buitenste swipe-scroller. - WEL: Implementeer onthulde acties en items in het zijmenu als echte focusbare DOM-bedieningselementen (
<button>,<a>). Als.SwipeReveal-contentfocusbaar is of focusbare bedieningselementen bevat, koppel je eenfocusin-listener aanevent.target.matches(':focus-visible')omscrollIntoView({ block: 'nearest', inline: 'start' })aan te roepen, zodat het zijvenster wordt gesloten als je teruggaat naar het contentvenster zonder dat dit invloed heeft op de aanwijzerinteracties. - Onderschep GEEN
wheel-,touchmove- ofpointermove-gebeurtenissen in JavaScript om de content handmatig te vertalen of scroll chaining te emuleren. Onderschepping van gebaren in de primaire thread blokkeert scrollen op basis van de compositor, verbreekt het systeemeigen scrollmomentum en vermindert de reactiesnelheid.
Reserve
Als een .SwipeReveal-container een .SwipeReveal-panel aan de linkerkant (inline-start) vóór .SwipeReveal-content plaatst, geven browsers die scroll-initial-target: nearest niet ondersteunen, de container in eerste instantie weer met de scroll-offset 0, waarbij het voorste deelvenster wordt getoond.
Als je Baseline-doel scroll-initial-target niet ondersteunt, detecteer je ondersteuning met CSS.supports('scroll-initial-target', 'nearest') en roep je scrollTo() aan met behavior: 'instant' op load, zodat containers met een voorloopvenster rechtstreeks naar .SwipeReveal-content springen zonder de pagina te scrollen:
Progressive enhancement
Behandel overscroll-behavior: chain en scrollbar-width: none als progressive enhancements zonder JavaScript-fallbacks:
- Automatische CSS-cascade-fallback: De beginwaarde van
overscroll-behaviorisauto. Browsers die het zoekwoordchainnog niet herkennen, negerenoverscroll-behavior: chaintijdens het parseren en behoudenauto. Omdatautoal scroll chaining naar bovenliggende scrollcontainers toestaat, werken zowel het veeggebaar om te onthullen als scroll chaining naar de bovenliggende container standaard in alle browsers. Ondersteunende browsers onderdrukken gewoon de ongewenste rubberband-bounce op de buitenste veegwrapper. containofnoneoverschrijven: Als een basisstylesheet of hulpprogrammaklasseoverscroll-behavior: containofnoneinstelt, declareer jeoverscroll-behavior: autoonmiddellijk vóóroverscroll-behavior: chain, zodat browsers diechainnog niet ondersteunen, nog steeds scrollen naar de bovenliggende container in plaats van deze vast te zetten.- Scrollbalk verbergen (
scrollbar-width: none): Het verbergen van de horizontale scrollbalk is puur cosmetisch. Als een oudere enginescrollbar-width: nonenegeert, blijft de container voor swipen om te onthullen volledig functioneel met een standaard scrollbalk.