Zum Inhalt springen
Seitensicht

Vorher-Nachher-Slider erstellen

Zwei Bilder übereinanderlegen und mit einem Regler vergleichen – für Handwerk, Renovierung, Restaurierung oder Bildbearbeitung. Du bekommst fertigen Code zum Einbauen, ohne Fremdbibliothek und ohne Abhängigkeit von einem Dienst.

Die beiden Bilder

Die Bilder bleiben auf deinem Rechner. Im erzeugten Code stehen ihre Dateinamen – lade sie auf deinen Server oder nimm gleich das ZIP-Paket unten.

Einstellungen

Richtung

0 % zeigt nur „nachher“, 100 % nur „vorher“.

Zum Einbauen · 150 Zeilen · 3.7 KB
<!-- Vorher-Nachher-Vergleich, erzeugt mit seitensicht.de -->
<style>
.ba-vergleich {
  /* Alle Stellschrauben an einer Stelle. */
  --ba-pos: 50%;
  --ba-linie: #ffffff;
  --ba-griff: #1f5fd6;
  --ba-radius: 12px;
  --ba-breite: 3px;

  position: relative;
  max-width: 900px;
  margin-inline: auto;
  border-radius: var(--ba-radius);
  overflow: hidden;
  /* Ohne diese Angabe springt das Layout, sobald die Bilder eintreffen. */
  line-height: 0;
  touch-action: none;
}

.ba-vergleich img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}

.ba-vergleich .ba-nachher {
  position: absolute;
  inset: 0;
  /* Der Zuschnitt folgt der Reglerposition. clip-path statt Breite, damit das Bild
     nicht gestaucht wird, sondern wirklich beschnitten. */
  clip-path: inset(0 0 0 var(--ba-pos));
}

.ba-vergleich .ba-linie {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--ba-pos);
  width: var(--ba-breite);
  transform: translateX(-50%);
  background: var(--ba-linie);
  pointer-events: none;
}

.ba-vergleich .ba-griff {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--ba-griff);
  border: var(--ba-breite) solid var(--ba-linie);
  display: grid;
  place-items: center;
  color: var(--ba-linie);
  font-size: 14px;
  line-height: 1;
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
}

.ba-vergleich .ba-griff::before {
  content: "\2194";
  font-size: 20px;
}

.ba-vergleich .ba-regler {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  /* Durchsichtig über allem: Der eigentliche Bedienweg für Maus, Finger und Tastatur. */
  opacity: 0;
  cursor: ew-resize;
  appearance: none;
  background: transparent;
}

.ba-vergleich .ba-regler:focus-visible {
  outline: none;
}

/* Sichtbarer Fokus am Griff statt am unsichtbaren Feld – sonst sieht niemand,
   wo die Tastatur gerade steht. */
.ba-vergleich:has(.ba-regler:focus-visible) .ba-griff {
  outline: 3px solid var(--ba-griff);
  outline-offset: 3px;
}

.ba-vergleich .ba-text {
  position: absolute;
  top: 12px;
  padding: 4px 10px;
  border-radius: calc(var(--ba-radius) / 2);
  background: rgb(0 0 0 / 0.6);
  color: #fff;
  font: 500 14px/1.4 system-ui, sans-serif;
  pointer-events: none;
}

.ba-vergleich .ba-text-vorher { left: 12px; }
.ba-vergleich .ba-text-nachher { right: 12px; }

@media (prefers-reduced-motion: reduce) {
  .ba-vergleich * { transition: none !important; }
}
</style>

<div class="ba-vergleich" id="ba14b16h" style="--ba-pos: 50%">
  <img class="ba-vorher" src="vorher.jpg" alt="Zustand vorher" width="" height="">
  <img class="ba-nachher" src="nachher.jpg" alt="Zustand nachher" width="" height="">
  <span class="ba-text ba-text-vorher">Vorher</span>
  <span class="ba-text ba-text-nachher">Nachher</span>
  <div class="ba-linie" aria-hidden="true"><span class="ba-griff"></span></div>
  <input
    class="ba-regler"
    type="range"
    min="0"
    max="100"
    value="50"
    aria-label="Vergleich zwischen Vorher und Nachher"
  >
</div>

<script>
(function () {
  'use strict';

  // Alle Regler der Seite auf einmal – so lassen sich beliebig viele einbauen.
  document.querySelectorAll('.ba-vergleich').forEach(function (kasten) {
    var regler = kasten.querySelector('.ba-regler');
    if (!regler || kasten.dataset.baBereit) return;
    kasten.dataset.baBereit = '1';

    function setzen() {
      kasten.style.setProperty('--ba-pos', regler.value + '%');
      // Für Screenreader: Der Prozentwert allein sagt nichts.
      regler.setAttribute('aria-valuetext', regler.value + ' Prozent sichtbar');
    }

    regler.addEventListener('input', setzen);
    setzen();
  });
})();
</script>

Warum das Ding ein Schieberegler ist

Unter dem Bild liegt ein ganz gewöhnliches <input type="range">, durchsichtig über die ganze Fläche gelegt. Das sieht man nicht, es ist aber der wichtigste Teil: Damit funktionieren Maus, Finger, Tastatur und Screenreader, ohne dass eine einzige Zeile Code dafür geschrieben werden müsste.

Die verbreiteten Umsetzungen horchen stattdessen auf Mausbewegungen. Sie sehen genauso aus – aber wer keine Maus benutzt, kommt an den Vergleich nicht heran. Bei einem Element, das die eigentliche Aussage der Seite trägt, ist das kein Randproblem.

Was du beachten solltest

Gleiches Seitenverhältnis. Der Vergleich lebt davon, dass sich die Bilder decken. Unterschiedliche Verhältnisse lassen den Regler verrutschen – die Seite warnt, sobald sie es bemerkt. Zuschneiden kannst du im Zuschneide-Werkzeug.

Gleicher Bildausschnitt. Zwei Fotos, die aus leicht verschiedenen Blickwinkeln entstanden sind, wirken beim Schieben unruhig. Ein Stativ oder wenigstens dieselbe Stelle machen den größten Unterschied.

Bilder vorher verkleinern. Es werden immer beide geladen, auch das zunächst verdeckte. Zwei Fotos in Originalgröße sind schnell mehrere Megabyte – der Skalierer und der WebP-Umwandler helfen.

Einbauen

Ein Schnipsel enthält alles zusammen und lässt sich in ein HTML-Element eines Baukastens einfügen. Getrennt ist sauberer, wenn du CSS und JavaScript ohnehin in eigenen Dateien hast. Ganze Seite ist zum Ausprobieren gedacht – Datei speichern, im Browser öffnen, fertig.

Das ZIP-Paket enthält die Seite samt beider Bilder, sodass die Pfade ohne Nacharbeit stimmen. Die Bilder in den Code einzubetten geht auch, macht ihn aber sehr groß und verhindert, dass der Browser sie zwischenspeichert.

Beliebig viele Regler auf einer Seite sind möglich – das Skript sucht sich alle und arbeitet nicht doppelt, wenn es zweimal eingebunden wird.