Developers often encounter a perplexing challenge when building responsive web designs: the apparent mismatch between the width reported by JavaScript’s $(window).width() and the viewport width used by CSS media queries. This phenomenon, where $(window).width() not the same as media query, can lead to frustrating debugging sessions and unexpected layout issues across different browsers and devices. Understanding the nuances behind this discrepancy is crucial for creating truly robust and adaptive user experiences. This article will delve into the technical reasons for these variations, explore how browsers interpret widths, and provide actionable strategies to ensure your JavaScript and CSS work in harmony, eliminating common pitfalls in responsive development.
Understanding the Discrepancy: Viewport vs. Device Width
The core of the confusion lies in how different browser mechanisms define and measure “width.” CSS media queries, particularly those using width or min-width/max-width, typically refer to the width of the layout viewport. This viewport is the area where your content is rendered, excluding browser UI elements but crucially, including any vertical scrollbars if they are present and taking up space. In contrast, JavaScript methods like $(window).width() (which in jQuery is often a wrapper for window.innerWidth) may or may not account for the scrollbar’s width, depending on the browser and its rendering engine.
This subtle difference can have significant implications. For instance, if a vertical scrollbar is present, it will occupy a certain number of pixels on the right side of the viewport. A CSS media query might evaluate the viewport’s width at 1024px, but $(window).width() could return 1007px if it deducts the scrollbar’s width (which is typically around 17px). This discrepancy means a breakpoint defined in CSS at 1024px might not trigger at the exact same point where your JavaScript condition based on $(window).width() does, leading to misaligned layouts or functionality.
The Role of Scrollbars
Scrollbars are a primary culprit when $(window).width() not the same as media query. Modern browsers handle scrollbars in one of two ways: either they overlay the content (like on macOS by default, or with custom scrollbar styling) or they push the content aside, reducing the available horizontal space. When scrollbars push content, they directly impact the layout viewport’s dimensions. Media queries, by design, typically ignore the scrollbar’s width and consider the full width of the viewport available for content, even if a scrollbar is visually present and consuming space. JavaScript, specifically window.innerWidth (and thus $(window).width()), often provides the actual pixel width available for content, which does account for the scrollbar’s width.
This behavior is standardized to some extent but can still vary slightly between browser engines. For example, some older Internet Explorer versions had different interpretations. Understanding this fundamental difference is key. The browser’s job is to render content, and sometimes the viewport needs to shrink slightly to accommodate these essential UI elements. If you’re encountering inconsistent breakpoints or JavaScript-driven animations, always consider the presence and behavior of scrollbars as a potential factor.
JavaScript’s Perspective: $(window).width() and window.innerWidth
When you use jQuery’s $(window).width(), you are essentially calling window.innerWidth under the hood for modern browsers and standards-compliant modes. This property returns the width of the layout viewport in pixels, including the width of the vertical scrollbar if it renders inside the viewport and occupies space. This is a critical distinction from document.documentElement.clientWidth or window.outerWidth. While outerWidth includes the entire browser window (including toolbars and borders), innerWidth focuses on the content area.
The consistent behavior of window.innerWidth across modern browsers makes it a reliable choice for measuring the actual available content width. However, its inclusion of the scrollbar’s width is precisely why it often diverges from media query evaluations. For developers, this means that any JavaScript logic that depends on specific width thresholds needs to explicitly account for the scrollbar’s presence, or use a different measurement that aligns more closely with CSS media queries. This might involve subtracting a typical scrollbar width (e.g., 17px) or using alternative properties like document.documentElement.clientWidth, which typically excludes scrollbars.
Differentiating width() from innerWidth
While $(window).width() largely mirrors window.innerWidth, it’s worth noting the subtle nuances and historical context. jQuery’s .width() method was designed to abstract away browser inconsistencies, especially in older browsers, ensuring a more consistent return value for the inner width of an element or the window. For the window object, it’s generally equivalent to window.innerWidth. According to the jQuery documentation, $(window).width() returns the “current computed width of the first element in the set of matched elements, including padding but not border or margin.” For the window, this translates to the viewport’s inner width.
To explicitly get the width without the scrollbar, a common technique in JavaScript is to use document.documentElement.clientWidth. This property returns the width of the root element (<html>) in CSS pixels, which typically excludes the scrollbar’s width, thus aligning more closely with how media queries evaluate the viewport. For instance, a study by Smashing Magazine highlighted various JavaScript techniques for accurate viewport measurements, often recommending a combination of properties for maximum compatibility. Choosing the right JavaScript property depends entirely on whether you need the width with or without the scrollbar.
CSS Media Queries: How They Interpret Width
CSS media queries operate on a different principle than JavaScript’s window.innerWidth. They are designed to respond to the dimensions of the initial containing block, which is the layout viewport. Crucially, CSS media queries are defined to disregard the width taken up by a vertical scrollbar. This means that if you have a media query like @media (max-width: 768px), it will trigger when the layout viewport is 768 pixels wide, regardless of whether a scrollbar is present and occupying space within that 768 pixels. This is a fundamental design choice that ensures responsive layouts behave predictably without being affected by the transient presence of scrollbars.
For example, if a user’s browser window is 785px wide, and a 17px scrollbar is present, the effective content area might be 768px. In this scenario, a JavaScript check using $(window).width() (or window.innerWidth) might return 768px, while a Question & Answer :
I am using Twitter Bootstrap on a project. As well as the default bootstrap styles I have also added some of my own
//My styles @media (max-width: 767px) { //CSS here }
I am also using jQuery to change the order of certain elements on the page when the width of the viewport is less that 767px.
$(document).load($(window).bind("resize", checkPosition)); function checkPosition() { if($(window).width() < 767) { $("#body-container .main-content").remove().insertBefore($("#body-container .left-sidebar")); } else { $("#body-container .main-content").remove().insertAfter($("#body-container .left-sidebar")); } }
The problem I am having is that the width calculated by $(window).width() and the width calculated by the CSS doesn’t seem to be the same. When $(window).width() returns 767 the css calculates it the viewport width as 751 so there seems to be a 16px different.
Does anyone know what is causing this and how I could solve the problem? People have suggested that the width of the scrollbar isn’t being taken into considering and using $(window).innerWidth() < 751 is the way to go. However ideally I want to find a solution that calculates the width of the scrollbar and that is consistent with my media query (e.g where both conditions are checking against the value 767). Because surely not all browsers will have a scrollbar width of 16px?
If you don’t have to support IE9 you can just use window.matchMedia() (MDN documentation).
function checkPosition() { if (window.matchMedia('(max-width: 767px)').matches) { //... } else { //... } }
window.matchMedia is fully consistent with the CSS media queries and the browser support is quite good: http://caniuse.com/#feat=matchmedia
UPDATE:
If you have to support more browsers you can use Modernizr’s mq method, it supports all browsers that understand media queries in CSS.
if (Modernizr.mq('(max-width: 767px)')) { //... } else { //... }