Phone 937.401.0915
Email Us

Responsive Web Design and JavaScript

If you haven’t read Ethan’s article, stop now, read it, and then come back here.

Now, let's talk about some responsive web design. For over a year now, we've been developing responsive websites almost exclusively at Sparkbox, and I’ve found something missing in our responsive tool belt – a good way to handle JavaScript.

Example

Recently, I was working on a secondary navigation with two sections. At media queries for smaller sized screens, the navigation had links to show and hide each section using jQuery animations. This posed a problem, however. After they were hidden, jQuery left style="display: none" on the elements. After resizing the browser and jumping to a media query for a larger sized screen, these elements should have been shown without the need for the additional links, but they were hidden because of the inline style. 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Media Query Testing</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<nav id="secondary-nav" class="secondary">
<div class="button-group">
<a href="#quick-links" class="mobile-button">Quick Links</a>
<a href="#site-search" class="mobile-button search-button">Site Search</a>
</div>
<ul id="quick-links" class="quick-links-menu secondary-menu">
<li><a href="#" title="">News</a></li>
<li><a href="#" title="">Account Info</a></li>
<li><a href="#" title="">FAQ</a></li>
<li><a href="#" title="">Help</a></li>
</ul>
<fieldset id="site-search" class="site-search secondary-menu">
<input class="search-field" type="search" name="site-search">
<input class="search-submit" type="submit" value="Search">
</fieldset>
</nav>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="js/mediaCheck.js"></script>
<script src="js/script.js"></script>
</body>
</html>
view raw html hosted with ❤ by GitHub

Now, I know that everyone likes to say that only developers change their browser sizes to see how things respond. There are real problems with this type of thinking. What if the user maximizes their window or makes it smaller based on the content? This might switch media queries, triggering a break in the secondary navigation. Here's another example that is pretty relevant – what if the controlling media query is somewhere around 900px? A simple orientation change of an iPad would cause these navigation elements to disappear. Oops. Do you care now?

Solution

The solution to this problem lies in the elusive matchMedia method; it provides events triggered by media queries.

In order to run code based on media queries, I'm using mediaCheck (a wrapper I've written around matchMedia to fire events when entering or leaving a designated media query) to trigger the cleanup code when entering larger media queries.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
$(function() {
function secondaryNavLinks( e ) {
var $toOpen = $( $( e.target ).attr( "href" ) + ":hidden" );
 
$( "#secondary-nav" ).children( ".secondary-menu:visible" ).slideUp();
$toOpen.slideDown();
 
e.preventDefault();
}
function secondaryNavCleanup() {
$( ".secondary-menu" ).removeAttr( "style" );
}
$( ".secondary a" ).on( "click", secondaryNavLinks );
mediaCheck({
media: '(min-width: 800px)',
entry: function() {
secondaryNavCleanup();
},
});
});
view raw js hosted with ❤ by GitHub

Now when the 600px media query is fired, it will remove the style attributes and all is well with the secondary nav.

mediaCheck provides a nice wrapper to easily turn code on and off based on media queries. Switching behaviors and cleaning up code between media queries is now made simple. JavaScript mischief managed.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
var mediaCheck = function(options) {
var mq,
matchMedia = window.matchMedia !== undefined;
if (matchMedia) {
mqChange = function(mq, options) {
if (mq.matches) {
options.entry();
} else {
options.exit();
}
};
createListener = function(mqDetails) {
mq = window.matchMedia(mqDetails.media);
mq.addListener(function() {
mqChange(mq, mqDetails);
});
mqChange(mq, mqDetails);
};
createListener(options);
}
};
view raw js hosted with ❤ by GitHub

Take it out for a test drive, kick the tires, and look under the hood. Feel free to ask questions and contribute, and let's move the web forward together.