jquerycssmedia-queriesbreakpoint-sass

Why is the window.width smaller than the viewport width set in media queries


I am quite puzzled and still unsure how to explain this in proper words. So far i've used and set up my media queries with Breakpoint. An used Breakpoint-variable looks like e.g.:

$menustatictofixed: min-width 900px;

$breakpoint-to-ems is set to true. I've laid out the page with all its Breakpoint variables based on the pixel values of the following jQuery snippet:

$('body').append('<div style="position: fixed; bottom: 0; right: 0; display: inline-block; font-weight: bold; padding: 5px 7px; border-radius: 5px 0 0 0; background: green; color: white; z-index: 9999;">Viewport width <span id="width"></span> px</div>');
var viewportWidth = $(window).width()
$('#width').text(viewportWidth);
$(window).resize(function() {
  var viewportWidth = $(window).width();
    $('#width').text(viewportWidth);
});

Everything looked proper and clean. But over the last one or two days i had issues setting up the last breakpoints for a pattern and to get things behave predictable. Somehow the things that appeared to add up clean and fine in the first place, which i logically highly doubt now, are in fact improper and somehow a mess. Cuz if you take a look at the following screenshot somehow the width of the window (in Chrome) differs to the width from the jQuery snippet utilising window.width. There isn't also a difference if i would replace window.width by window.innerWidth to rule out scrollbars eventually. The only way to receive proper results is by adding 15 pixels to the equation:

var viewportWidth = $(window).width() + 15;

Is the only issue in the whole equation that window.width is the wrong choice of function and it would be better to go with e.g. document.documentElement.clientWidth or something else or ... ? And for what the 15 pixels are standing for which fixed the problem above in a bit hacky way? Best regards Ralf


Solution

  • The answer is scrollbars, and the solution is tricky.

    Media queries are interesting. They don't behave precisely the same across browser, meaning using them can sometimes not be so easy. In -webkit/-blink on OS X and IE10 on Win8 for example, scrollbars are overlaid onto the page. In -moz, however, scrollbars are not overlaid onto the page. The best way to get the "viewable area" of the page is the following line of vanilla JavaScript (assuming you have a valid HTML document):

    document.body.parentNode.clientWidth
    

    What this will do is find the body element, find it's parent (which in a valid document will be the html element), and then find it's width after rendering. This will give you the width you're interested in seeing. It's also a useless width to have.

    Why, you may ask, is having the actual client width useless? It's not because it varies from browser to browser, because it does. It's because that's not what the width media queries are actually testing! What width media queries test is window.innerWidth, which is what you're in essence using now.

    So what is the solution to this problem? Well I'd say the solution is to use content based media queries instead of device based media queries and be OK with some wiggle room in your queries (especially if that wiggle room is approx. 15px). If you haven't already, read the articles Vexing Viewports and A Pixel Identity Crisis to get an idea as to why a potential 15px shimmer in your MQ definitions isn't the worst thing in the world (there are probably bigger fish to fry).

    So, in conclusion continue using $breakpoint-to-ems: true and choose your media queries based on when content breaks to window.innerWidth as it's the only sane way of handling cross-browser issues. From a cursory glance of various issues, it appears as if most browsers and OSes are moving to an overlay scrollbar (as of Firefox 24 every OS X browser will have one, Ubuntu's Unity UI introduced them), so in an effort to be future friendly, I'd suggest not worrying about the scroll bar offset and be OK with sites looking slightly different across browser. Remember, as Ethan Marcotte so eloquently put:

    The Web is an Inherently Unstable Medium