CSS Media Queries (erster Teil)

Permalink

Bereits vor mehr als einem Jahr hatte ich ein paar störrige Geräte die meine CSS3 Media Queries nicht wie erwartet angewendet haben. Also habe ich mir damals ein paar einfache Tests geschrieben mit denen ich den damaligen Fuhrpark an Geräten durchtesten konnte. Mit der Zeit ist die Anzahl der getesteten Eigenschaften immer weiter angewachsen, so das ich jetzt eine recht umfangreiche Testsuite habe. Damit kann ich recht schnell Aussagen darüber treffen welche Arten von Queries anwendbar sind.

Wer es auch mal ausprobieren will kann es gerne tun – Die Bedienung braucht eher keine Erklärung. Stattdessen möchte ich ein paar Sätze zur Umsetzung verlieren.

Referenzwerte

Wer etwas Testen will braucht erst einmal ein paar Basisinformationen und Referenzwerte. Anhand dieser kann man einschätzen ob die einzelnen Test überhaupt anwendbar sind. Ein Test auf eine Breite von genau 1024 Pixeln ist natürlich nicht anwendbar bei einem Display mit 1280 Pixeln.

Einige der benötigten Werte sind, teils mit jQuery, recht einfach zu ermitteln. Andere Werte müssen etwas umständlicher beschafft werden:

Displaygröße

Mit screen.width und screen.height bestimmt man Breite und Höhe des Bildschirms.

Dokumentengröße

$(document).width() und $(document).height() liefern die Maße des gesamten Dokuments inklusive der Teile die nicht im Viewport sichtbar sind.

Farbtiefe

Die Farbtiefe der Monitors bekommt man mit screen.colorDepth. Allerdings muss man ein berücksichtigen das einige Browser (Chrome und Opera) den Alpha-Kanal mit zur Bitzahl hinzunehmen und andere (Firefox und Internet Explorer) nicht. Außerdem muss man beachten das die CSS-Spezifiktion nicht die Summe aller Bits einer Farbe sondern die Anzahl an Bits pro Farbkanal (normalerweise Rot, Grün und Blau) heranzieht. Wie sich die ermittelte Anzahl der Bits auf die einzelnen Kanäle verteilt kann man zum Beispiel in Artikel Farbtiefe der Wikipedia ablesen.

Device Pixel Ratio

Bei modernen Geräten mit hochauflösenden Displays werden durch den Browser die eigentlichen Pixel des Geräts gemäß eines bestimmten Verhältnisses auf sogenannte CSS-Pixel umgerechnet. Dies geschieht um die Webseiten beim Rendern nicht zu klein erscheinen zu lassen. Dieses Verhältnis zwischen Geräte- und CSS-Pixeln lässt sich mit window.devicePixelRatio ermitteln.

Andere Werte müssen etwas umständlicher beschafft werden:

Viewport

Die Maße des Viewports sind nicht, wie es auf den ersten Blick scheint, durch zwei jQuery Methoden zu bestimmen. $(window).width() und $(window).width() liefern zwar schon annähernd richtige Werte, beinhalten aber leider nicht eventuell vorhandene Scrollbalken. Diese gehören aber gemäß Spezifikation zum Viewport. Um die richtigen Werte zu erhalten muss man also die Scrollbalken mittels CSS-Deklaration overflow:hidden. unterdrücken. Die beiden folgenden Funktionen gewähren auf diese Weise zugriff auf die korrekte Breite bzw. Höhe des Viewports:

function getViewportWidth() {
	$("body").css("overflow", "hidden");
	var width = $(window).width();
	$("body").css("overflow", "auto");
	return width;
}

function getViewportHeight() {
	$("body").css("overflow", "hidden");
	var height = $(window).height();
	$("body").css("overflow", "auto");
	return height;
}

Auflösung

Um die Auflösung des Bildschirms, also die Pixeldichte zu bestimmen greift man zu einem kleinen Trick. Man erzeugt ein DIV-Element und gibt diesem mittels CSS eine Breite und Höhe von genau einem Zoll (oder Zentimeter) vor. Danach kann man mit den Methoden width() und height()@ aus jQuery den entsprechenden Wert in Pixeln bestimmen. Diese beiden Werte entsprechen dann der Anzahl der CSS-Pixel pro Zoll (bzw. Zentimeter). Bei hochauflösenden Displays muss man hier aber aufpassen. Durch die zusätzliche Abstraktionsschicht zwischen Gerätepixeln und CSS-Pixeln misst man hier “falsch”. Das lässt sich aber leicht korrigieren da der passende Faktor ja durch window.devicePixelRatio bekannt ist.

Die folgende Funktion berechnet die physikalische Pixeldichte. Eine der beiden Einheiten dpi (Dots per Inch) oder dpcm (Dots per Centimeter) lässt sich über den ersten Parameter auswählen. Über den zweiten Wert lässt sich einstellen wie das Verhalten der Funktion ist sollten die vertikale und horizontale Pixeldichte voneinander abweichen. Das kann passieren wenn die einzelnen Pixel nicht genauso hoch wie breit sind. Über die Werte min und max des optionalen Parameters extremum enthält man entweder das Minimum oder Maximum von vertikaler und horizontaler Pixeldichte.

function getResolution(unit, extremum) {
  $("head").append($("<style id='detectorstyle'><style>"));
  $("#detectorstyle").text(
       ".detector {             "
     + "    border:none;        "
     + "    margin:0;           "
     + "    padding:0;          "
     + "    position:absolute;  "
     + "    left:-3000px;       "
     + "    top:-3000px;        "
     + "}                       "
     + "#dpidetector {          "
     + "    width:1in;          "
     + "    height:1in;         "
     + "}                       "
     + "#dpcmdetector {         "
     + "    width:1cm;          "
     + "    height:1cm;         "
     + "}                       "
     + "</style");
  $("body").append($("<div id='dpidetector' class='detector'></div>"));
  $("body").append($("<div id='dpcmdetector' class='detector'></div>"));

  var pixeldensity = NaN;
  switch(extremum) {
    case "max":
      switch(unit) {
        case "dpi":
          pixeldensity = math.max($("#dpidetector").width(),$("#dpidetector").height());
          break;
        case "dpcm":
          pixeldensity =  math.max($("#dpcmdetector").width(),$("#dpcmdetector").height());
          break;
      }
      break;
    case "min":
      switch(unit) {
        case "dpi":
          pixeldensity =  math.min($("#dpidetector").width(),$("#dpidetector").height());
          break;
        case "dpcm":
          pixeldensity =  math.min($("#dpcmdetector").width(),$("#dpcmdetector").height());
          break;
      }
      break;
    default:
      if ($("#dpidetector").width() === $("#dpidetector").height()) {
        switch(unit) {
          case "dpi":
            pixeldensity =  $("#dpidetector").width();
            break;
          case "dpcm":
            pixeldensity =  $("#dpcmdetector").width();
            break;
        }
      }
      else {
        pixeldensity =  NaN;
      }
  }

  $("#detectorstyle").remove();
  $("#dpidetector").remove();
  $("#dpcmdetector").remove();

  return pixeldensity * ("devicePixelRatio" in window ? devicePixelRatio : 1);
};

Ausblick

Anhand der so ermittelten Werte ist es nun möglich zu entscheiden ob eine Media Query wirken müsste oder nicht. Außerdem lassen sich mit diesen Werten die Media Queries dynamisch an die aktuellen Gegebenheiten beim Tester anpassen so das sie, wenn der Browser die jeweilige Eigenschaft unterstützt, alle anwendbar sein sollten.

Ein zweiter Artikel greift dieses Thema noch einmal auf und stellt das eigentliche Testszenario vor.