Mobile-First mit matchMedia unter WordPress

Bei einem neuen Projekt wollte ich es ausprobieren: den Mobile-First-Ansatz mit WordPress. Hier steht, wie ich das gemacht habe.[toc]

Vorbereitungen

Ein neues Theme für den Kunden war nötig. Ich habe _s von Automattic dazu benutzt. Letztlich ist es das “CSS-Reset” für WordPress. In die header.php habe ich direkt nach

<?php wp_head(); ?>

meinen eigenen do_action()-Befehl eingefügt:

<?php
wp_head();
do_action( 'match_media' );
?>

Und zwar, damit sichergestellt ist, dass das spätere JavaScript auch sofort nach dem Header gelesen und ausgeführt wird.

Der JavaScript-Code

Nach ein bisschen ‘googelei’ bin ich auf einen Beitrag Christian Heilmann bezügl. Conditional Loading gestolpert. Er beschreibt ein kleines JavaScript welches genau die Funktion durchführen sollte, die ich haben wollte:

(function(){
  var queries = document.
                querySelectorAll('.mediaquerydependent'),
      all = queries.length,
      cur = null,
      attr = null;
  while (all--) {
    cur = queries[all];
    if (cur.dataset.media &&
        window.matchMedia(cur.dataset.media).matches) {
      for (attr in cur.dataset) {
        if (attr !== 'media') {
          cur.setAttribute(attr, cur.dataset[attr]);
        }
      }
    }
  }
}());

Damit das Ganze auch funktioniert, wenn man das Browserfenster verkleinert bzw. vergrößert, habe ich das Script wie folgt ergänzt:

(function () {
	var ffm = function () {
		var queries = document.querySelectorAll( '.freising-matchmedia' ),
						all = queries.length,
						cur = null,
						attr = null;
				while ( all-- ) {
					cur = queries[all];
					if ( cur.dataset.media && window.matchMedia( cur.dataset.media ).matches ) {
						for ( attr in cur.dataset ) {
							if ( attr !== 'media' ) {
								cur.setAttribute( attr, cur.dataset[attr] );
							}
						}
					}
				}
			};

	if ( window.addEventListener ) {
		window.addEventListener( 'resize', ffm, false );
	} else if ( elem.attachEvent ) {
		window.attachEvent( "onresize", ffm );
	} else {
		window["onresize"] = ffm;
	}

	ffm();

}());

Das Script sucht nach vorkommen mit der CSS-Klasse .matchmedia und schleust sie letztlich durch die window.matchMedia-Funktion.

CSS-Manipulation

Wir fügen die CSS-Dateien, wie gewohnt mittels wp_enqueue_style() ein:

<?php
add_action( 'wp_enqueue_scripts', 'mein_enqueue_scripts' );
function mein_enqueue_scripts() {

	wp_enqueue_style( 'test_theme-mobile', get_stylesheet_directory_uri() . '/css/theme-mobile.css' );
	wp_enqueue_style( 'test_theme-tablet', get_stylesheet_directory_uri() . '/css/theme-tablet.css', array( 'theme-mobile' ), false, 'screen and (min-width: 768px)' );
}
?>

Nun entsteht im HTML-Code folgendes:

<!DOCTYPE html>
<html lang="de-DE">
<head>
  ...
  <link rel='stylesheet' id='test_theme-mobile-css'  href='http://localhost/test/wp-content/themes/test/style.css?ver=3.9.2' type='text/css' media='all' />
  <link rel='stylesheet' id='test_theme-tablet-css' href='http://localhost/freisingerleben/wp-content/themes/freisingerleben/css/tablet.css?ver=3.9.2' type='text/css' media='screen and (min-width: 768px)' />
</head>
<body>
...

Das hilft uns aber wenig. Denn fast alle Browser laden die CSS-Dateien trotzdem auch (auch wenn sie – wie in Falle von Chrome – mit niedriger Priorität gehandhabt werden).

Deswegen manipulieren wir die entsprechenden Dateien wie folgt:

<?php
add_filter( 'style_loader_tag', 'mein_style_loader_tag', 10, 2 );
function mein_style_loader_tag( $tag, $handle ) {
	if ( false !== strpos( $handle, 'test_theme-tablet' ) ) {
		$tag = str_replace( 'href=', 'class=\'matchmedia\' href=\'\' data-href=', $tag );
		$tag = str_replace( 'media=', 'data-media=', $tag );
		return $tag;
	}
	return $tag;
}
?>

Hier werden drei Dinge gemacht:

  1. Es wird eine CSS-Klasse .matchmedia eingefügt.
  2. href="" wird zu data-href="" umgewandelt.
  3. media="" wird zu data-media="" umgewandelt.

Nun können die CSS-Dateien nicht mehr geladen werden da keine URL vorhanden ist. Stattdessen übernimmt das JavaScript den Rest.

Der finale JavaScript Code

In die functions.php muss jetzt lediglich noch der eigentliche JavaScript Code:

/**
 * Doing the matchMedia to avoid loading all the stuff that is not needed.
 */
add_action( 'matchmedia', 'matchmedia', 99999 );
function matchmedia() {
	?>
	<script>
		(function () {
			var ffm = function () {
				var queries = document.querySelectorAll( '.matchmedia' ),
						all = queries.length,
						cur = null,
						attr = null;
				while ( all-- ) {
					cur = queries[all];
					if ( cur.dataset.media && window.matchMedia( cur.dataset.media ).matches ) {
						for ( attr in cur.dataset ) {
							if ( attr !== 'media' ) {
								cur.setAttribute( attr, cur.dataset[attr] );
							}
						}
					}
				}
			};

			if ( window.addEventListener ) {
				window.addEventListener( 'resize', ffm, false );
			} else if ( elem.attachEvent ) {
				window.attachEvent( "onresize", ffm );
			} else {
				window["onresize"] = ffm;
			}

			ffm();

		}());
	</script>
<?php
}

Kompatibilität

Übrigens: MatchMedia sollte funktionieren unter

  • IE >= 10
  • Firefox >= 30
  • Chrome >= 27
  • Safari >= 5.1
  • Opera >= 23
  • iOs-Safari >= 6.1
  • Android >= 4
  • Chrome for Android >= 36

Schreiben Sie einen Kommentar