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@mediaMy script.innerWidth.clientWidth
IE 9500500500483
IE 6, 7, 8NA484undefined484
FF 17 Win500500500483
FF 17 Mac500500500485
Chrome 24 Win483483500483
Safari 5 Win483483500483
Opera 12 Win500500500483
Opera 12 Mac500500500485
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.