Die meisten modernen Webseiten verwenden responsive Webdesign-Techniken, um sicherzustellen, dass sie gut aussehen, lesbar sind und auf Geräten mit jeder Bildschirmgröße nutzbar bleiben, d.h. auf Handys, Tablets, Laptops, Desktop-PCs, Fernsehern, Projektoren und mehr.

Seiten, die diese Techniken verwenden, haben eine einzige Vorlage, die das Layout je nach Bildschirmgröße anpasst:

  • Kleinere Bildschirme zeigen in der Regel eine lineare, einspaltige Ansicht, in der Bedienelemente wie Menüs durch Anklicken von (Hamburger-)Symbolen aktiviert werden.
  • Größere Bildschirme zeigen mehr Informationen, vielleicht mit horizontal ausgerichteten Seitenleisten. UI-Steuerelemente wie z. B. Menüpunkte können immer sichtbar sein, um den Zugriff zu erleichtern.

Ein wichtiger Bestandteil des responsiven Webdesigns ist die Implementierung einer CSS– oder JavaScript-Medienabfrage, um die Größe des Geräts zu erkennen und automatisch das passende Design für diese Größe anzuzeigen. Wir werden nun erörtern, warum diese Abfragen wichtig sind und wie man damit arbeitet, aber zuerst wollen wir uns mit responsivem Design im Allgemeinen beschäftigen.

Warum ist Responsive Design wichtig?

Es ist unmöglich, ein einziges Seitenlayout anzubieten und zu erwarten, dass es überall funktioniert.

Als Mobiltelefone in den frühen 2000er Jahren erstmals einen rudimentären Internetzugang erhielten, erstellten die Betreiber von Seiten oft zwei oder drei verschiedene Seitenvorlagen, die sich grob an der mobilen und der Desktop-Ansicht orientierten. Das wurde immer unpraktischer, als die Vielfalt der Geräte exponentiell zunahm.

Heute gibt es zahlreiche Bildschirmgrößen, von winzigen Armbanduhren bis hin zu riesigen 8-K-Monitoren und mehr. Selbst wenn du nur Mobiltelefone betrachtest, können aktuelle Geräte eine höhere Auflösung haben als viele Low-End-Laptops.

Auch die Nutzung von Mobiltelefonen ist inzwischen größer als die von Desktop Computern. Wenn du deine Seite nicht auf eine bestimmte Gruppe von Nutzern ausrichtest, kannst du davon ausgehen, dass die Mehrheit der Besucher von einem Smartphone aus auf die Seite zugreift. Geräte mit kleinen Bildschirmen sind nicht länger ein nachträglicher Gedanke und sollten von Anfang an berücksichtigt werden, auch wenn die meisten Webdesigner/innen, Entwickler/innen und Kund/innen weiterhin einen Standard-PC verwenden.

Google hat die Bedeutung von Mobilgeräten erkannt. Seiten werden in der Google-Suche besser gerankt, wenn sie benutzerfreundlich sind und auf einem Smartphone gut funktionieren. Gute Inhalte sind nach wie vor wichtig, aber eine langsam ladende Seite, die sich nicht an die Bildschirmgröße deiner Nutzer/innen anpasst, könnte deinem Unternehmen schaden.

Und schließlich solltest du auf Barrierefreiheit achten. Eine Seite, die für jeden funktioniert, egal welches Gerät er oder sie benutzt, erreicht ein größeres Publikum. In vielen Ländern ist Barrierefreiheit gesetzlich vorgeschrieben, aber auch wenn das in deinem Land nicht der Fall ist, solltest du bedenken, dass mehr Besucher/innen zu mehr Conversions und höherer Rentabilität führen.

Wie funktioniert Responsive Design?

Die Grundlage des responsive Designs sind Media-Queries: eine CSS-Technologie, die Stile entsprechend der Ausgabeart (Bildschirm, Drucker oder sogar Sprache), der Bildschirmabmessungen, des Seitenverhältnisses, der Geräteausrichtung, der Farbtiefe und der Zeigergenauigkeit anwenden kann. Media-Queries können auch die Vorlieben der Nutzer/innen berücksichtigen, z. B. reduzierte Animationen, Hell/Dunkel-Modus und höherer Kontrast.

In den gezeigten Beispielen werden Medienabfragen nur für die Bildschirmbreite verwendet, aber Seiten können noch viel flexibler sein. Weitere Informationen findest du in der vollständigen Liste der Optionen auf MDN.

Die Unterstützung für Media Queries ist hervorragend und wird von den Browsern seit mehr als einem Jahrzehnt unterstützt. Nur der IE8 und darunter haben keine Unterstützung. Sie ignorieren Stile, die von Media Queries angewendet werden, aber das kann manchmal ein Vorteil sein (mehr dazu im Abschnitt Best Practices weiter unten).

Es gibt drei Standardmethoden, um Stile mit Media Queries anzuwenden. Bei der ersten werden bestimmte Stylesheets in den HTML-Code geladen. Der folgende Tag lädt zum Beispiel das Stylesheet wide.css, wenn ein Gerät einen Bildschirm hat, der mindestens 800 Pixel breit ist:

<link rel="stylesheet" media="screen and (min-width: 800px)" href="wide.css" />

Zweitens können Stylesheets mit einer @import at-Regel bedingt in CSS-Dateien geladen werden:

/* main.css */
@import url('wide.css') screen and (min-width: 800px);

Normalerweise wendest du Media Queries in Stylesheets an, indem du einen @media CSS at-rule-Block verwendest, der bestimmte Stile verändert. Zum Beispiel:

/* default styles */
main {
  width: 400px;
}

/* styles applied when screen has a width of at least 800px */
@media screen and (min-width: 800px) {
  main {
    width: 760px;
  }
}

Entwickler können die Regeln für Medienabfragen anwenden, die erforderlich sind, um das Layout einer Seite anzupassen.

Bewährte Verfahren für Media Query

Als die ersten Media Queries entwickelt wurden, entschieden sich viele Seiten für eine Reihe starrer Layouts. Dies ist konzeptionell einfacher zu gestalten und zu programmieren, weil es eine begrenzte Anzahl von Seitenvorlagen effektiv reproduziert. Ein Beispiel:

  1. Bei einer Bildschirmbreite von weniger als 600 Pixel wird ein 400 Pixel breites, mobiles Layout verwendet.
  2. Bei einer Bildschirmbreite zwischen 600px und 999px wird ein 600px breites Tablet-ähnliches Layout verwendet.
  3. Bei einer Bildschirmbreite von mehr als 1.000px wird ein 1000px breites desktopähnliches Layout verwendet.

Die Technik ist fehlerhaft. Die Ergebnisse auf sehr kleinen und sehr großen Bildschirmen können schlecht aussehen, und die CSS-Wartung kann erforderlich sein, wenn sich Geräte und Bildschirmgrößen mit der Zeit ändern.

Eine bessere Option ist die Verwendung eines Mobile-First-Fluid-Designs mit Haltepunkten, die das Layout bei bestimmten Größen anpassen. Im Wesentlichen verwendet das Standardlayout die einfachsten Stile für kleine Bildschirme, die Elemente in linearen vertikalen Blöcken positionieren.

Zum Beispiel <article> und <aside> innerhalb eines <main> Containers:

/* default small-screen device */
main {
  width: 100%;
}

article, aside {
  width: 100%;
  padding: 2em;
}

Hier ist das Ergebnis in allen Browsern – auch in sehr alten, die keine Media Queries unterstützen:

Beispiel-Screenshot ohne Media Query-Unterstützung
Beispiel-Screenshot ohne Media Query-Unterstützung

Wenn Media Queries unterstützt werden und der Bildschirm eine bestimmte Breite überschreitet, z.B. 500px, können die <article> und <aside> Elemente horizontal positioniert werden. In diesem Beispiel wird ein CSS-Raster verwendet, bei dem der primäre Inhalt etwa zwei Drittel der Breite einnimmt und der sekundäre Inhalt das restliche Drittel:

/* larger device */
@media (min-width: 500px) {
  main {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 2em;
  }

  article, aside {
    width: auto;
    padding: 0;
  }
}

Hier ist das Ergebnis auf größeren Bildschirmen:

Beispiel-Screenshot mit Media Query-Unterstützung
Beispiel-Screenshot mit Media Query-Unterstützung

Media Query-Alternativen

Responsive Designs können in modernem CSS auch mit neueren Properties umgesetzt werden, die das Layout von sich aus anpassen, ohne die Viewport-Dimensionen zu prüfen. Zu den Optionen gehören:

  • calc, min-width, max-width, min-height, max-height, und die neuere Eigenschaft clamp können alle Dimensionen definieren, die Elemente entsprechend bekannter Grenzen und des verfügbaren Platzes vergrößern.
  • Die Viewport-Einheiten vw, vh, vmin, und vmax können die Größe von Elementen anhand von Bildschirmabmessungen bestimmen.
  • Text kann in CSS-Spalten dargestellt werden, die erscheinen oder verschwinden, wenn es der Platz erlaubt.
  • Die Größe von Elementen kann mit den Dimensionen min-content, fit-content, und max-content an die Größe ihrer untergeordneten Elemente angepasst werden.
  • CSS Flexbox kann Elemente umbrechen – oder nicht umbrechen – wenn sie den verfügbaren Platz überschreiten.
  • CSS-Grid-Elemente können mit proportionalen Bruchteilen von Einheiten dimensioniert werden. Die CSS-Funktion repeat kann in Verbindung mit minmax, auto-fit und auto-fill verwendet werden, um den verfügbaren Platz aufzuteilen.
  • Die neuen und (derzeit) experimentellen CSS-Container-Abfragen können auf den teilweisen Platz reagieren, der einer Komponente innerhalb eines Layouts zur Verfügung steht.

Diese Optionen würden den Rahmen dieses Artikels sprengen, sind aber oft praktischer als grobe Media-Queries, die nur auf die Bildschirmgröße reagieren können. Wenn du ein Layout ohne Media-Queries erstellen kannst, wird es wahrscheinlich weniger Code benötigen, effizienter sein und weniger Wartung erfordern.

Dennoch gibt es Situationen, in denen Media Queries die einzige brauchbare Layout-Option sind. Sie sind unverzichtbar, wenn du andere Bildschirmfaktoren wie Seitenverhältnisse, Geräteausrichtung, Farbtiefe, Zeigergenauigkeit oder Benutzereinstellungen wie reduzierte Animationen und Hell/Dunkel-Modus berücksichtigen musst.

Musst du Media Queries in JavaScript verwenden?

Bis jetzt haben wir hauptsächlich über CSS gesprochen. Das liegt daran, dass die meisten Layout-Probleme allein mit CSS gelöst werden können – und sollten.

Es gibt jedoch Situationen, in denen es praktisch ist, eine JavaScript-Medienabfrage anstelle von CSS zu verwenden, z. B. wenn:

  • Eine Komponente, z. B. ein Menü, hat auf kleinen und großen Bildschirmen unterschiedliche Funktionen.
  • Der Wechsel zwischen Hoch- und Querformat wirkt sich auf die Funktionalität einer Web-App aus.
  • Ein touchbasiertes Spiel muss das <canvas>-Layout ändern oder die Steuerungsschaltflächen anpassen.
  • Eine Web-App richtet sich nach den Vorlieben der Nutzer/innen, z. B. Dunkel-/Hell-Modus, reduzierte Animationen, Grobheit der Berührung usw.

Die folgenden Abschnitte zeigen drei Methoden, die Media-Queries – oder Media-Query-ähnliche Optionen – in JavaScript verwenden. Alle Beispiele geben einen Status-String zurück:

  • small view = ein Bildschirm mit einer Breite unter 400 Pixeln;
  • medium view = ein Bildschirm mit einer Breite zwischen 400 und 799 Pixeln; und
  • large view = ein Bildschirm mit einer Breite von 800 Pixeln oder mehr.

Option 1: Überwache die Viewport-Dimensionen

Das war die einzige Möglichkeit in den dunklen Tagen, bevor Media Queries implementiert wurden. JavaScript hörte auf „Resize“-Ereignisse des Browsers, analysierte die Abmessungen des Ansichtsfensters mit window.innerWidth und window.innerHeight (oder document.body.clientWidth und document.body.clientHeight in alten IEs) und reagierte entsprechend.

Dieser Code gibt den berechneten small, medium oder large String auf der Konsole aus:

const
  screen = {
    small: 0,
    medium: 400,
    large: 800
  };

// observe window resize
window.addEventListener('resize', resizeHandler);

// initial call
resizeHandler();

// calculate size
function resizeHandler() {

  // get window width
  const iw = window.innerWidth;
 
  // determine named size
  let size = null;
  for (let s in screen) {
    if (iw >= screen[s]) size = s;
  }

  console.log(size);
}

Du kannst dir hier eine funktionierende Demonstration ansehen. (Wenn du einen Desktop-Browser verwendest, öffne diesen Link in einem neuen Fenster, damit du die Größe leichter ändern kannst. Mobile Nutzer können das Gerät drehen).

Das obige Beispiel prüft die Größe des Viewports, wenn die Größe des Browsers geändert wird, stellt fest, ob sie klein, mittel oder groß ist, und legt dies als Klasse für das body-Element fest, wodurch die Hintergrundfarbe geändert wird.

Die Vorteile dieser Methode sind:

  • Sie funktioniert in jedem Browser, der JavaScript ausführen kann – sogar in alten Anwendungen.
  • Du erfährst die genauen Maße und kannst entsprechend reagieren.

Die Nachteile:

  • Es ist eine alte Technik, die viel Code erfordert.
  • Ist sie zu genau? Musst du wirklich wissen, wann die Breite 966px und wann 967px beträgt?
  • Möglicherweise musst du die Abmessungen manuell mit einer entsprechenden CSS-Medienabfrage abgleichen.
  • Nutzer können die Größe des Browsers schnell ändern, so dass die Handler-Funktion jedes Mal neu ausgeführt werden muss. Dadurch können ältere und langsamere Browser überlastet werden, indem das Ereignis gedrosselt wird. Es kann nur einmal alle 500 Millisekunden ausgelöst werden.

Zusammenfassend lässt sich sagen, dass du die Viewport-Abmessungen nicht überwachen solltest, es sei denn, du hast sehr spezifische und komplexe Anforderungen an die Größe.

Option 2: Definiere und überwache eine benutzerdefinierte CSS Property (Variable)

Dies ist eine etwas ungewöhnliche Technik, die den Wert einer benutzerdefinierten Property-Zeichenkette in CSS ändert, wenn eine Medienabfrage ausgelöst wird. Benutzerdefinierte Properties werden von allen modernen Browsern (außer dem IE) unterstützt.

Im folgenden Beispiel wird die benutzerdefinierte Property --screen in einem @media-Codeblock auf „small“, „medium“ oder „large“ gesetzt:

body {
  --screen: "small";
  background-color: #cff;
  text-align: center;
}

@media (min-width: 400px) {
 
  body {
    --screen: "medium";
    background-color: #fcf;
  }
 
}

@media (min-width: 800px) {
 
  body {
    --screen: "large";
    background-color: #ffc;
  }
 
}

Der Wert kann in CSS allein mit einem Pseudo-Element ausgegeben werden (beachte aber, dass er in einfachen oder doppelten Anführungszeichen stehen muss):

p::before {
  content: var(--screen);
}

Du kannst den Wert der benutzerdefinierten Property mit JavaScript abrufen:

const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen');

Das ist allerdings nicht ganz richtig, denn der zurückgegebene Wert enthält alle Leer- und Anführungszeichen, die nach dem Doppelpunkt im CSS definiert sind. Die Zeichenkette wird „large“ sein, also ist ein wenig Aufräumen nötig:

// returns small, medium, or large in a string
const screen = getComputedStyle(window.body)
                 .getPropertyValue('--screen')
                 .replace(/\W/g, '');

Du kannst dir hier eine funktionierende Demonstration ansehen. (Wenn du einen Desktop-Browser verwendest, öffne diesen Link in einem neuen Fenster, damit du die Größe leichter ändern kannst. Mobile Nutzer können das Gerät drehen.)

Das Beispiel prüft den CSS-Wert alle zwei Sekunden. Es erfordert ein wenig JavaScript-Code, aber er ist notwendig, um nach Änderungen zu suchen – du kannst nicht automatisch erkennen, dass sich der Wert der benutzerdefinierten Property mit CSS geändert hat.

Es ist auch nicht möglich, den Wert in ein Pseudo-Element zu schreiben und die Änderung mit einem DOM Mutation Observer zu erkennen. Pseudo-Elemente sind kein „echter“ Teil des DOMs!

Die Vorteile:

  • Es ist eine einfache Technik, die hauptsächlich CSS verwendet und mit echten Media Queries übereinstimmt.
  • Alle anderen CSS Properties können gleichzeitig geändert werden.
  • Du musst keine JavaScript-Medienabfrage-Strings duplizieren oder parsen.

Der größte Nachteil ist, dass du nicht automatisch auf eine Änderung der Viewport-Abmessungen des Browsers reagieren kannst. Wenn der Nutzer sein Handy vom Hoch- ins Querformat dreht, erfährt das JavaScript das nicht. Du kannst zwar häufig nach Änderungen fragen, aber das ist ineffizient und führt zu der Zeitverzögerung, die du in unserer Demonstration siehst.

Die Überwachung der benutzerdefinierten CSS Properties ist eine neue Technik, die aber nur dann sinnvoll ist, wenn:

  1. Das Layout kann zum Zeitpunkt des ersten Renderns einer Seite festgelegt werden. Ein Kiosk oder ein Point-of-Sale-Terminal ist eine Möglichkeit, aber diese haben wahrscheinlich feste Auflösungen und ein einziges Layout, so dass JavaScript-Media-Queries irrelevant werden.
  2. Auf der Seite oder in der App laufen bereits häufig zeitbasierte Funktionen, wie z. B. eine Spielanimation. Die benutzerdefinierte Property könnte gleichzeitig überprüft werden, um festzustellen, ob Layoutänderungen erforderlich sind.

Option 3: Die matchMedia-API verwenden

Die matchMedia API ist etwas ungewöhnlich, aber sie ermöglicht es dir, eine JavaScript-Medienabfrage zu implementieren. Sie wird von den meisten Browsern ab IE10 aufwärts unterstützt. Der Konstruktor gibt ein MediaQueryList-Objekt zurück, das eine Property matches hat, die für die jeweilige Medienabfrage true oder false ausgibt.

Der folgende Code gibt true aus, wenn die Breite des Browserfensters 800px oder mehr beträgt:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
console.log( mqLarge.matches );

Ein „Change“-Ereignis kann auf das MediaQueryList-Objekt angewendet werden. Es wird jedes Mal ausgelöst, wenn sich der Status der Property „matches“ ändert: Sie wird true (über 800px), nachdem sie vorher false (unter 800px) war, oder umgekehrt.

Der empfangenden Handler-Funktion wird das MediaQueryList-Objekt als erster Parameter übergeben:

const mqLarge  = window.matchMedia( '(min-width: 800px)' );
mqLarge.addEventListener('change', mqHandler);

// media query handler function
function mqHandler(e) {
 
  console.log(
    e.matches ? 'large' : 'not large'
  );
 
}

Der Handler wird nur ausgeführt, wenn sich die Property matches ändert. Er wird nicht ausgeführt, wenn die Seite zum ersten Mal geladen wird. Du kannst die Funktion also direkt aufrufen, um den Ausgangszustand zu bestimmen:

// initial state
mqHandler(mqLarge);

Die API funktioniert gut, wenn du dich zwischen zwei verschiedenen Zuständen bewegst. Um drei oder mehr Zustände zu analysieren, z. B. small, medium und large, ist mehr Code erforderlich.

Beginne mit der Definition eines Bildschirmzustandsobjekts mit zugehörigen MatchMedia-Objekten:

const
  screen = {
    small : null,
    medium: window.matchMedia( '(min-width: 400px)' ),
    large : window.matchMedia( '(min-width: 800px)' )
  };

Es ist nicht notwendig, ein MatchMedia-Objekt für den small Zustand zu definieren, da der medium-Event-Handler beim Wechsel zwischen small und medium ausgelöst wird.

Für die medium und large Ereignisse können dann Ereignis-Listener gesetzt werden. Diese rufen dieselbe Handler-Funktion mqHandler() auf:

// media query change events
for (let [scr, mq] of Object.entries(screen)) {
  if (mq) mq.addEventListener('change', mqHandler);
}

Die Handler-Funktion muss alle MediaQueryList-Objekte überprüfen, um festzustellen, ob small, medium oder large gerade aktiv ist. Die Abgleiche müssen in der Reihenfolge der Größe durchgeführt werden, da eine Breite von 999px sowohl auf medium als auch auf large zutreffen würde – nur der größte sollte „gewinnen“:

// media query handler function
function mqHandler() {
 
  let size = null;
  for (let [scr, mq] of Object.entries(screen)) {
    if (!mq || mq.matches) size = scr;
  }
 
  console.log(size);
 
}

Du kannst dir hier eine funktionierende Demonstration ansehen. (Wenn du einen Desktop-Browser verwendest, öffne diesen Link in einem neuen Fenster, damit du die Größe leichter ändern kannst. Mobile Nutzer können das Gerät drehen.)

Die Anwendungsbeispiele sind:

  1. Medienabfragen in CSS zum Festlegen und Anzeigen einer benutzerdefinierten Property (wie in Option 2 oben gezeigt).
  2. Identische Media-Queries in matchMedia-Objekten, um Dimensionsänderungen in JavaScript zu überwachen. Die JavaScript-Ausgabe ändert sich dann genau zur gleichen Zeit.

Die wichtigsten Vorteile der matchMedia-API sind:

  • Sie ist ereignisgesteuert und verarbeitet Medienabfrageänderungen effizient.
  • Sie verwendet identische Media Query Strings wie CSS.

Die Nachteile:

  • Die Verarbeitung von zwei oder mehr Medienabfragen erfordert mehr Überlegungen und Codelogik.
  • Wahrscheinlich musst du Media Query Strings sowohl im CSS- als auch im JavaScript-Code duplizieren. Das kann zu Fehlern führen, wenn du sie nicht synchron hältst.

Um falsche Medienabfragen zu vermeiden, könntest du Design-Tokens in deinem Build-System verwenden. Media Query Strings werden in einer JSON-Datei (oder einer ähnlichen Datei) definiert und die Werte werden bei der Erstellung in den CSS- und JavaScript-Code eingefügt.

Zusammenfassend lässt sich sagen, dass die matchMedia-API wahrscheinlich der effizienteste und praktischste Weg ist, eine JavaScript-Medienabfrage zu implementieren. Sie hat zwar einige Tücken, ist aber in den meisten Fällen die beste Option.

Zusammenfassung

Intrinsische CSS-Größenoptionen werden immer praktikabler, aber Media Queries bleiben für die meisten Seiten die Grundlage des responsiven Webdesigns. Sie werden immer notwendig sein, um komplexere Layouts und Nutzerpräferenzen wie den Hell/Dunkel-Modus zu behandeln.

Versuche, Media-Queries möglichst auf CSS zu beschränken. Wenn du keine andere Wahl hast, als dich in die Welt von JavaScript zu begeben, bietet die matchMedia-API zusätzliche Kontrolle für JavaScript-Medienabfragekomponenten, die zusätzliche dimensionsbasierte Funktionen benötigen.

Hast du weitere Tipps für die Implementierung einer JavaScript-Medienabfrage? Teile sie in den Kommentaren mit!

Craig Buckler

Freelance UK web developer, writer, and speaker. Has been around a long time and rants about standards and performance.