Bei Wischgesten zum Einblenden können Nutzer eine Zeile oder einen Inhaltsbereich im Vordergrund horizontal verschieben, um ausgeblendete UI-Elemente wie kontextbezogene Aktionsbuttons (Anpinnen, Stummschalten, Archivieren, Löschen) oder ein sekundäres seitliches Menü einzublenden, ohne dass automatisch eine Aktion aktiviert wird. Anstatt eine Aktion während des Wischens auszuführen, wird der Container geöffnet und zeigt interaktive Steuerelemente an, die der Nutzer prüfen und aktivieren oder durch Zurückwischen schließen kann.
Informationen zu einer Wischgeste über die gesamte Breite, mit der ein Element sofort über einen Schwellenwert hinaus verschoben und entfernt wird, finden Sie unter Zum Entfernen wischen. Informationen zum Einblenden einer ausgeblendeten Kopfzeile oben in einem vertikalen Scrollcontainer finden Sie unter Zum Einblenden ziehen.
Implementierung
Egal, ob Sie Aktionsbuttons für ein Listenelement oder ein seitliches Menü neben einem scrollbaren Bereich einblenden, die Mechanik ist identisch: Verpacken Sie den Vordergrundinhalt und das ausgeblendete Steuerfeld in einem äußeren horizontalen Scroll-Container, der mit CSS Scroll Snap (scroll-snap-type: x mandatory) und overscroll-behavior: chain konfiguriert ist. Die native Scroll-Engine des Browsers verarbeitet Momentum, Einrasten, Scrollen mit Tastaturfokus und Scrollverkettung ohne JavaScript-Gesten-Listener.
Schritt 1: Swipe-Wrapper, Inhalt und eingeblendetes Steuerfeld markieren
Platzieren Sie zuerst den Vordergrundinhalt und dann die eingeblendeten Steuerelemente (Aktionsbuttons oder ein seitliches Menü) im Scroll-Container .SwipeReveal. Da der Inhalt in der DOM-Reihenfolge an erster Stelle steht, beginnt der Scroll-Container natürlich beim Scroll-Offset 0, wobei der Bereich rechts außerhalb des Bildschirms ausgeblendet ist.
<!-- 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>
Schritt 2: Horizontalen Snap-Container und overscroll-behavior: chain konfigurieren
Wenn Sie ein Element in einen äußeren Scroll-Container einfügen, nur um einen Swipe-to-Reveal-Effekt zu erzielen, wird dieser äußere Wrapper zu einem Zwischen-Scroller im Dokument (oder einem anderen übergeordneten Scroll-Container).
Die vier Schlüsselwörter von overscroll-behavior steuern unabhängig voneinander, ob übermäßiges Scrollen an einen übergeordneten Scrollcontainer weitergegeben wird (Scrollverkettung) und ob der Container lokale Overscroll-Affordanzen wie elastisches Gummiband-Stretching oder Randglühen anzeigt (lokaler Grenzeneffekt):
| Wert | Scroll-Chaining (wird an übergeordnetes Element weitergegeben) | Lokaler Begrenzungseffekt (Gummiband / Leuchten) | Rolle bei „Wischen zum Aufdecken“ |
|---|---|---|---|
auto |
Ja | Ja | Standardverhalten; sollte für innere Inhaltsscroller beibehalten werden, bei denen ein normales Bounce-Feedback gewünscht ist, aber für den äußeren Swipe-Wrapper vermieden werden, da es dort dazu führt, dass das Menü springt |
chain |
Ja | Nein | Verwende es für den äußeren .SwipeReveal-Scroller, damit überschüssiges Scrollen in den übergeordneten Scroller (z. B. das Dokument) übergeht, ohne dass das Menü oder die Aktionsleiste springt. |
contain |
Nein | Ja | Fängt das Scrollen innerhalb des Containers ab, während lokal weiterhin ein Bouncing erfolgt |
none |
Nein | Nein | Blockiert sowohl das lokale Springen als auch die Scrollverkettung zu übergeordneten Elementen |
Machen Sie .SwipeReveal zu einem zweispaltigen Raster (grid-template-columns: 100% max-content), in dem .SwipeReveal-content an start und .SwipeReveal-panel an end andockt:
.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;
}
}
Wenn Sie auch Steuerelemente auf der linken Seite (
inline-start) mit drei Spalten (grid-template-columns: max-content 100% max-content) oder zwei Spalten (max-content 100%) anzeigen möchten, platzieren Sie das führende.SwipeReveal-panelvor.SwipeReveal-content. Da.SwipeReveal-contentden Wertscroll-initial-target: nearesthat, ruht der Scrollcontainer anfangs auf der Inhaltsspalte und nicht auf dem führenden Bereich.
Schritt 3: Fokussierbare Inhaltsbereiche bei Tastaturfokus wieder einblenden
Da die Schaltflächen oder Links in .SwipeReveal-panel im Ruhezustand nicht auf dem Bildschirm zu sehen sind, wird beim Drücken der Tabulatortaste mit einer Tastatur automatisch .SwipeReveal gescrollt, um das fokussierte Steuerelement anzuzeigen. Da .SwipeReveal-content jedoch 100% der Breite des äußeren Containers einnimmt, bleibt der größte Teil davon auch bei geöffneter Seitenleiste im Scrollport. Wenn Sie also Shift+Tab drücken, um den Fokus wieder auf .SwipeReveal-content (oder auf ein fokussierbares Element darin) zu verschieben, wird der Track nicht automatisch zurück zu 0 gescrollt.
Achten Sie auf focusin für .SwipeReveal-content und prüfen Sie event.target.matches(':focus-visible'), damit der Tastaturfokus auf dem Bereich oder einem untergeordneten Element auf .SwipeReveal-content zurückgesetzt wird, ohne dass Zeigerklicks oder Touch-Drag-Vorgänge beeinträchtigt werden:
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 und Fallstricke
- Verwenden Sie
scroll-snap-type: x mandatoryanstelle vonproximity. Beiproximitykann ein leichtes Wischen dazu führen, dass die Aktionsbuttons oder das seitliche Menü im Ruhezustand teilweise sichtbar bleiben. - DO set
overscroll-behavior: chainon the outer horizontal swipe-to-reveal scroller (.SwipeReveal) so scroll can chain into the parent scroller without the revealed menu or action strip bouncing. - DO leave
overscroll-behavior: auto(the default) on inner scrollable content panes (.SwipeReveal-content.is-scrollable) where you want normal bounce feedback to happen while still chaining horizontal swipes to the outer swipe scroller. - DO implement revealed actions and side-menu items as real focusable DOM controls (
<button>,<a>). When.SwipeReveal-contentis focusable or contains focusable controls, pair afocusinlistener withevent.target.matches(':focus-visible')to callscrollIntoView({ block: 'nearest', inline: 'start' })so tabbing back into the content pane closes the side panel without affecting pointer interactions. - Fange KEINE
wheel-,touchmove- oderpointermove-Ereignisse in JavaScript ab, um den Inhalt manuell zu übersetzen oder Scroll-Chaining zu emulieren. Das Abfangen von Gesten im Hauptthread blockiert das vom Compositor gesteuerte Scrollen, unterbricht die native Scroll-Dynamik und beeinträchtigt die Reaktionsfähigkeit.
Fallback
Wenn ein .SwipeReveal-Container ein .SwipeReveal-panel auf der linken Seite (inline-start) vor .SwipeReveal-content platziert, wird es in Browsern, die scroll-initial-target: nearest nicht unterstützen, zuerst mit dem Scroll-Offset 0 gerendert, wobei das führende Steuerfeld sichtbar ist.
Wenn Ihr Baseline-Ziel scroll-initial-target nicht unterstützt, können Sie die Unterstützung mit CSS.supports('scroll-initial-target', 'nearest') erkennen und scrollTo() mit behavior: 'instant' auf load aufrufen, damit Container mit einem führenden Steuerfeld direkt zu .SwipeReveal-content springen, ohne dass die Seite gescrollt werden muss:
Progressive Verbesserung
Behandle overscroll-behavior: chain und scrollbar-width: none als progressive Verbesserungen ohne JavaScript-Fallbacks:
- Automatischer CSS-Kaskaden-Fallback: Der Anfangswert von
overscroll-behavioristauto. Browser, die das Keywordchainnoch nicht erkennen, ignorierenoverscroll-behavior: chainbeim Parsen und behaltenautobei. Daautobereits die Scrollverkettung zu übergeordneten Scrollcontainern ermöglicht, funktionieren sowohl die Wischgeste zum Einblenden als auch die Scrollverkettung zum übergeordneten Element in allen Browsern standardmäßig. Unterstützende Browser unterdrücken einfach den unerwünschten Rubber-Band-Bounce auf dem äußeren Wisch-Wrapper. containodernoneüberschreiben: Wenn in einem Basis-Stylesheet oder einer Dienstprogrammklasseoverscroll-behavior: containodernonefestgelegt ist, deklarieren Sieoverscroll-behavior: autounmittelbar voroverscroll-behavior: chain, damit Browser, diechainnoch nicht unterstützen, das Scrollen weiterhin mit dem übergeordneten Element verknüpfen, anstatt es zu blockieren.- Scrollleiste ausblenden (
scrollbar-width: none): Das Ausblenden der horizontalen Scrollleiste ist rein kosmetisch. Wenn eine ältere Enginescrollbar-width: noneignoriert, bleibt der Container zum Aufdecken per Wischen mit einer Standard-Scrollleiste voll funktionsfähig.