What size is my viewport?

Using JavaScript to get the actual viewport width

Responsive Web design is making a positive and lasting impact to the web as we know it. Along with most change comes new scenarios. I think it's safe to say that supporting a flexible viewport is at the top of the list. While CSS if often enough, sometimes JavaScript is required. Currently, there are several outdated and incorrect ways to get the viewport size using JavaScript. Many people use the innerWidth of the browser window, while others use the clientWidth of the documentElement. Others use jQuery's $(window).width(), which ends up just using documentElement.clientWidth. Unfortunately, these approaches are flawed if what you are wanting is the actual size used in CSS media queries. To reduce confusion, I will refer to this as "CSS viewport".

Browser differences

Cross-browser compatibility still remains an issue with many features, CSS viewport size is no exception. For example, WebKit browsers change the size of their CSS viewport when scroll bars are visible, while most other browsers do not. Since window.innerWidth remains constant regardless of the scroll bar state, it's not a good option for use with Chrome or Safari. Additionally, Internet Explorer 6, 7, and 8 do not support window.innerWidth. On the other hand, document.documentElement.clientWidth can change based on the scroll bar state and therefore is not a good option for Internet Explorer, Firefox, or Opera.

window.innerWidth vs. documentElement.clientWidth

If you are using window.innerWidth to get the size the viewport irrespective to scroll bar state, then great. Or, if you are using documentElement.clientWidth to get what the current usable viewport is, then awesome. However, if you are using one or the other thinking it will give you the actual CSS, then it's probably time to start updating your sites.

The following table compares the potential values of innerWidth and clientWidth to the actual CSS viewport width.

Browser .innerWidth .clientWidth
IE == <=
Firefox == <=
Opera == <=
Chrome >= ==
Safari >= ==
Table only represents versions of browsers that support CSS media queries.

The following table shows a more detailed comparison on how browsers treat the CSS viewport when scroll bars are visible.

Browser @media My script .innerWidth .clientWidth
IE 9 500 500 500 483
IE 6, 7, 8 NA 484 undefined 484
FF 17 Win 500 500 500 483
FF 17 Mac 500 500 500 485
Chrome 24 Win 483 483 500 483
Safari 5 Win 483 483 500 483
Opera 12 Win 500 500 500 483
Opera 12 Mac 500 500 500 485
Some browsers were omitted from the list that no longer use scroll bars that consume layout space.

Is there a good solution?

A quick web search will bring back lots of misinformation on obtaining the CSS viewport size. After looking at the actual data, the quick and dirty solution to the problem is to use user agent detection to toggle between innerWidth and clientWidth, but as we all know UA detection should only be used as a last resort (unless you are actually targeting the UA). I analyzed a lot of different JavaScript objects to find a way to calculate the CSS viewport size cross browser without UA sniffing, but with no luck. That however did not cause me to give up; if it can be done with CSS, then it should be able to be done with JavaScript. This concept led me to my current solution.

Solution

I found that if scroll bars are not visible, then window.innerWidth and documentElement.clientWidth will both return the correct value. However, when scroll bars are visible some additional detection is required. The current version of the script injects a CSS media query to test if documentElement.clientWidth is equal to the current CSS viewport size. If they are equal, it uses the value, however if they are not equal, it falls back to use window.innerWidth. There are currently no known unsupported browsers and should be ready for production use. Check out the project homepage to download the uncompressed or minified versions of the script. Enjoy!

Get the Script

Related

And yes, I'm aware that I posted a similar script in an earlier post but thought it deserved a bit more of an explanation.

20 Responses

Leave a comment or contact me on Twitter @TysonMatanich

  1. Wow. This looks Great! I will put this to work immediately. Would it be possible to make a version that uses EMs?

    • I tried creating an EM based solution but ran into some issues with how browsers treat viewport (differently of course) when the page is zoomed. Will post as I learn more.

  2. I used to write:

    function viewportDimensions() {
    	var e = window;
    	var a = 'inner';
    	if (!('innerWidth' in window)) {
    		a = 'client';
    		e = document.documentElement || document.body;
    	}
    	return {width: e[a+'Width'], height: e[a+'Height']}
    };

    Good to know that a more embracing code is available! :) Thanks.

  3. Chris Bowes

    Thanks Tyson, I haven’t taken a close look yet but this is excellent work. Now all we need is some javacript that can detect the actual physical width of a device. That would be handy. But this is overdue and something I have been meaning to look at for a while, so thank you.

    Re: the above comment I don’t think Javascript can return em values(?), only pixels so I guess you would need to calculate the em values based on the body or html font-size percentage and work it from there

  4. Mike Anderson

    This is great. I’ve been doing this is a much simpler and less robust way. Very glad you worked out all the issues and published it. Thanks.

  5. If you are in JavaScript, a sure method could be adding an element in the page in position absolute/fixed, top,left,right and bottom at 0 and then looking at its size.
    I never tried it but that seems a reasonable hack that could be useful in case like browser zooming on mobile.

  6. zilijen

    Totally wrong results on my HTC Desire default browser. It says 320×533 and my device resolution is 480×800. How come?

    • The HTC Desire has a device pixel ratio of 1.5 so its viewport is smaller than its device resolution. This is similar to the iPhone 4 that has a device resolution of 640×960 but a viewport of 320×480.

  7. wazz

    1. did you mean to say “miss information”? (under “Is there a good solution?”) [misinformation]
    2. missing the word “if” (under “Solution”): “…however[, if] they are not equal…”

  8. Alan

    This is interesting, and yet another thing to make me rip want to rip the hair out of browser developers.

    Here’s my thing. I’ve got a page (in Chrome) that is very big (scroll bars) when wide enough (>=640px) but shrinks right down when <640px (no scroll bars). When I'm playing with my browser and changing the width, there's a spot where the div's offsetWidth is 7, then 0, which changes whether it's using clientWidth or innerWidth. I'm not really describing it well, but because the media queries are switching from a big screen with scroll bars to a small screen without scrollbars, there's still a spot where the JavaScript *thinks* the screen is wider than it really is.

  9. Geoff

    Am I missing something?
    Why would you need to know the width of every client with such accuracy?

    I’ve never liked responsive sites that jump to a different layout when the client is resized. Or look totally different on a smaller device.

    I’ve done a bit of research into responsive and adaptive sites to try and find a nice way of doing it. A good example I’ve found is at imindit.co.uk. They seem to use a more fluid approach. It even works in ie 7 and 8 although I’m not sure how. They must be using JavaScript for that I guess. Which maybe means they are using this code after all!!!!!!!!! Which makes this comment kinda redundant.

    • Sometimes the accuracy is not needed, but other times it is. For example, on the Microsoft.com homepage there are four sizes of the logo: small 1x, small 2x, large 1x, and large 2x (the 2x images are used for high ppi displays). If the larger image was used in the smaller view the logo would appear blurry. Some people might be fine with this, however I felt it was important for the logo to appear its best.

  10. Mik

    Thanks for your plugin. I tested it but I didn’t seem to do what I expected, I still have a logged width of 655px in Firefox (19.0.2) where I have 640px on Chrome (25). Maybe I missed sg. I loaded the script and add this in my js init file:

    $(window).resize(function() {
        var width = viewportSize.getWidth();
        var height = viewportSize.getHeight();
        console.log(width, height);
    });
  11. Btw, Opera 15 (the current version) behaves like Chrome, i.e. the viewport width does not take the scroll bar into account.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>