Back to All

Firing Responsive jQuery Functions based on CSS Media Queries Rather than Window Width

Updated:

There are some great options for managing Javascript when using CSS media queries in a responsive website. MediaCheck, jRespond, and Breakpoints.js all allow you to fire javascript functions based on customizable breakpoints in browser width. However, recently I was working on a small site with only a single function to be called at a smaller browser size, in conjunction with a media query, and thought I'd forgo one of these scripts and manage my change using a jQuery window width measurement.

The Problem: jQuery $(window).width() and CSS3 Media Queries do not always match.

Initially I was using the below code:

$(window).resize(function(){
	if ($(window).width() <= 800){	
		// do something here
	}	
});

When viewing the site in Firefox, I noticed a small difference in the width at which my media query and javascript were firing. Roger Johansson recently wrote up a great article documenting the inconsistent treatment of vertical scrollbars in media query widths. This inconsistency causes a small difference between browsers in the actual window width when a media query fires, which in turn causes media query and jQuery width measurements to not match in some popular browsers: Firefox, IE, and Opera. Here is a code example showing the difference between the jQuery window width and the media query width measurement.

The solution: use jQuery to test for a changed CSS property, rather than the browser width

Rather than the measuring the screen width on resize of the window, I check for a css rule that is changed by the media query. This way, regardless of the how the browser treats the scrollbar, the media query will fire at the same time.

Suppose the "sampleClass" class has a float:left rule before the media query, and a float:none rule after, on window resize I check for the change in that rule.

The jQuery

$(window).resize(function(){	
	if ($(".sampleClass").css("float") == "none" ){
		// your code here
	}
});

The CSS

.sampleClass {float:left;}
@media only screen and (max-width: 800px){
	.sampleClass {float:none;}
}

34 Comments:

Vinod Patil

It’s great, thank you ! :)

Nathan

Thank you! This sounds exactly what I have been looking for. By the way, I love the way your site here responds by making the navigation menu animate when the width is smaller. I might just use that for my site. Thanks for the inspiration and keep up the good work!

Anthony Cullen

Worked a treat targeting z-index of a relatively-positioned element. Thanks!

Lukasz

Smart and clean move. I’m going to use it with Bootstrap by checking .container’s width. Thanks for sharing.

supermegapollo

Awesome!! Thx dude ;)

Julian

Perfect !
jquery has ever the answer ;)

Alan A

What a great idea! Using it right now.

Ryan

That’s neat a way to go about things, I’ve got some media queries/scripts to galvanize!  Thanks for the tip!

Fergus

This is bad because it intrinsically links behaviour (JS) to content (HTML). Say you (or a colleague) may in the future need to remove the JS hook element (the floated left class). Doing so would render this JS function obsolete (unused code), and would furthermore interfere with the intentional behaviour of the responsive design. We should always be mindful to keep a ‘separation of concerns’ when it comes to these sorts of problems.

Chris Arasin

Hi Fergus, good point. My counter argument for this method is that in many cases, we may want that intrinsic link between the javascript and the markup. For example, if there’s a piece of content that at larger screen sizes functions as a slideshow but then at smaller sizes breaks out of the slideshow into a scrollable format, you would want the javascript to change exactly when the css modifies the layout.

But to your point, yes a totally arbitrary selection of css rule to check against may lead to a problem down the road.

Fergus

That intrinsic link can (and I think should) be communicated through employing semantically similar JS hooks and CSS classes/IDs. For instance, I might have some buttons with class=“random_button”. If I want to attach JS behaviour to those buttons I would add an additional class of ‘js_random_button’. Decoupling of behaviour and presentation, as you say, prevents problems later on, but it also serves as a helpful visual indicator of intent. Given that class, it will be immediately obvious to others that the element/s is/are also used in a JS script. There are many advantages to this type of css selector allocation.

David

What a clever, elegant solution. Thanks!

Martin

Yes.. but happens when you initially load the page on a mobile device, so there’s no actual browser resize happening? Then this jQuery never fires.

Chris Arasin

You can do a check outside of the window.resize function as well.

Ünsal Korkmaz

Seriously i love those type of solutions ^^ Thank you

Dagobert Renouf

Superb idea, I love how it makes it basically impossible to fail, and as it’s supposed to be linked to the css anyway… this is perfect, thank you so much for posting this!

Bill Criswell

Awesome idea. I think you guys would find Jeremy Keith’s approach very interesting as well. I hacked up a little example for you.

http://jsbin.com/ehawax/1

Mark

Thanks! This was a big help.

Justin

Why not just give ‘html’ a width of 100% on your stylesheet and use jQuery to pull the width. Another bonus of doing that is it will account for the width of the scroll bar. Just a thought…

Working example: http://buffolark.com/sandbox/jq-ss-loader/

Chris Arasin

Thanks Justin, I like that method too.

Shaun W.

Thanks!

Using bootstrap.

Set a display: hidden; div with a .responsiveTell class then set content: ‘xs’; in each media query

Then tested for .css(‘content’) === ‘xs/sm/md/lg’ in jquery

Frank Folsche

Hey, nice script but there where a few things not working for me:
like Chris Arasin mentioned you also need to check outside of the resize function otherwise the function does not get called on mobile divices. (tip: you can check this easily in chrome canary)
also the resize function checks for both vertical and horizontal changes, and you only need to detect the horizontal change, this was a problem for me because I was changing the height of the window and the function kept on firing.
I’m now using this:
$(document).ready(function () {
  $(window).resize(function () {
      if ($(window).height() == height) return;
      mobile();
  });
  mobile();
});
function mobile() {
  if ($(“body”).css(“left”) == “3px”) {//mobile test == <500
      smallMobile();
  };
}

Pawel

Doesn’t work for me unfortunately, using html5 boilerplate with jQuery 1.10.2 ... without the line “if ($(”.sampleClass”).css(“float”) == “none” ){” my code fires up no problems, though. is there any special requirement for this function to work? this is my full code:
$(document).ready(function(){
      if ($(“p.test”).css(“font-size” == “3em” ){
          $(“body”).click(function() {
          $(this).css(‘background’,‘red’);
          });
        });
      });

James

Anyone got any thoughts on what a good CSS property to be applied to the body that wouldn’t go anything? That way that property wont need to be overridden if the site is restyled, and the body (unlike other elements) will always be on the page.

Umair Hafeez

Great :-) works like a charm…

Gunnar Bittersmann

This has little to do with jQuery. You can easily do the same in vanilla JavaScript.

Septi

@Pawel, you have a little mistake right here:

  if ($(“p.test”).css(“font-size” == “3em” ){

Notice the == operator is inside the .css() method parentheses. What happens here is instead of first acquiring the font size and then comparing it with “3em”, your code instead compares the literal string “font-size” with “3em”, then essentially calls $(“p.test”).css(false). Oh, and it appear you lost a close-paren for the if condition somewhere along the way :) So, here’s how that line should look like:

  if ($(“p.test”).css (“font-size”) == “3em”) {

Yudhistira Mauris

Very smart move, I didn’t even think about that. What is the thing that causes the difference between CSS3 media query and jQuery width()? Is it a browser bug?

Chris Arasin

Hi Yudhistira,
It had to do with how browsers were handling the scroll bar on a page when measuring the width. Looking back at the example now, it’s looking like maybe new browsers have corrected this and made the measurement consistent.

Eric D. Greene

Simple and elegant solution, I like it.

eric

Much cleaner then constantly using js to do millions off if statements as the window is resized wow good job.

Stephen T

This is slick. I set my conditionals for both document ready AND window resize. Minimize the liability of the depency on an attribute value by having the script look at something really consistent, like a mobile/desktop menu.

$(window).resize(function(){
      // enable and close when resizing to mobile…
if ($(“header nav.desktop”).css(“display”) == “none” ){
  $( “.accordion” ).accordion({
  collapsible: true,
  active: false,
  disabled: false
  });
}

// ... or open and disable when resizing to tablet/desktop
else if ($(“header nav.desktop”).css(“display”) == “block” ){
  $( “.accordion” ).accordion({
  collapsible: false,
  disabled: true
  });
}
});

jnz31

yo. thanks for this, loving it. made a css element with css content and let it fire only once it changes:
css:
#responsive { content:“XS”; display:none;}

@media screen and (min-width: 450px) {
#responsive {content:“S”;}
}

@media screen and (min-width: 600px) {
#responsive {content:“M”;}
}

@media screen and (min-width: 960px) {
#responsive {content:“L”;}
}
js:
var responsiveValue, responsiveChange;
(responsiveChange = function() {
if ( $(”#responsive”).css(“content”) !== responsiveValue ) {
  if ( $(”#responsive”).css(“content”) === ‘“L”’ ) {
  console.log( “HEY! ITS LARGE” );
  //do stuff only @ large..
  } else {
  console.log( $(”#responsive”).css(“content”) );
  // else…
  }
  responsiveValue = $(”#responsive”).css(“content”);
}
})();
$(window).resize( function() {
responsiveChange();
});

dont forget to place an element with id responsive to your DOM. thanks!!!

Chris Arasin

Nice. You’re probably also going to want to fire responsiveChange() on document as well.

Add a Comment

* Required Fields
background background
background background background
background background background background