affordable web development services
| About Us | Services | Download | Order | Support | Subscribe to News Feed Follow us on Twitter Join us on Facebook |
 

Getting window size and scroll bars position in JavaScript/DHTML

Here I attempted to create a reliable method to read the client size of the of the browser's window and the position of the scroll bars. Of course this wouldn't be a problem if all browsers were well written and followed the standards. As we all know it's not the case -- different versions of different browsers under different platforms in different modes have own ideas on how this simple information should be provided. Adjusting the script for different browsers can be real pain when after making code work in one browser you find out it no longer works in another.

Enough with the cries, let's try to find the solution. Here is the plan:

  1. Collect as many different methods as possible
  2. Create testing environment
  3. Try every method on as many browsers as possible (different platforms, modes etc.)
  4. Analyze the results
  5. Come up with the solution

1. Different methods to get the window size and scroll bar position

I've searched the network and found three different methods browsers use to provide the information about window size and scroll bar position:

ParameterwindowdocumentElementbody
Window Widthwindow.innerWidthdocument.documentElement.clientWidthdocument.body.clientWidth
Window Heightwindow.innerHeightdocument.documentElement.clientHeightdocument.body.clientHeight
Horizontal Scrollwindow.pageXOffsetdocument.documentElement.scrollLeftdocument.body.scrollLeft
Vertical Scrollwindow.pageYOffsetdocument.documentElement.scrollTopdocument.body.scrollTop

2. Testing Environment

This page reports the results returned by all twelve functions. The grid is added to the page so the values can be compared to actual window size and scroll bars positions. DOCTYPE DTD statement at the beginning of the file can be temporarily commented out to read the values in non DTD mode. pixel.gif images can be commented out to see the values without the scroll bars in use.

3. Testing Results

The table below contains the testing results. Most recent versions of the browsers were used for testing. Even though different version of the same browser may behave differently the results obtained seem to be sufficient for developing the robust solution (see analysis in the next section).
browser platform mode window.innerWidth
window.innerHeight
window.pageXOffset
window.pageYOffset
document.documentElement.clientWidth
document.documentElement.clientHeight
document.documentElement.scrollLeft
document.documentElement.scrollTop
document.body.clientWidth
document.body.clientHeight
document.body.scrollLeft
document.body.scrollTop
IE Win32 w. DTD undef
undef
undef
undef
ok
ok
ok
ok
doc. width
doc. height
0
0
IE Win32 w/o. DTD undef
undef
undef
undef
0
0
0
0
ok
ok
ok
ok
IE Mac all undef
undef
undef
undef
undef
undef
undef
undef
ok
ok
ok
ok
Firefox Win32/Mac w. DTD +scroller
+scroller
ok
ok
ok
ok
ok
ok
ok
doc. height.
ok
ok
Firefox Win32/Mac w/o. DTD + scroller
+scroller
ok
ok
doc.width
doc.height
0
0
ok
ok
ok
ok
Netscape Win32 w. DTD + scroller
+scroller
ok
ok
ok
ok
ok
ok
ok
doc. height.
ok
ok
Netscape Win32 w/o. DTD + scroller
+scroller
ok
ok
0
0
0
0
ok
ok
ok
ok
Opera Win32/Mac all + scroller
+scroller
ok
ok
ok
doc.height
ok
ok
ok
ok
ok
ok
Safari Mac all ok
ok
ok
ok
ok
doc.height
0
0
ok
doc.height
ok
ok

4. Analysis

So we have bad news and good news:

It's clear that when object, or method is not defined or if method returns null then we should look somewhere else but the tricky part is that method can return what seems to be the correct value while it's not what we actually need. Looking at what those values are we can see that in case of the window size it's window size + scroll bar width (15-16px) or the document size or just zero. In case of scroll bar position it's just zero.

5. Solution

Sure we could check the browser name and then determine the name of the method we should use based on the table like the one above, but disadvantages of the method are pretty clear

My approach is:

  1. Read all three values if corresponding methods and objects are defined
  2. Discard undefined (null) values
  3. Determine smallest non zero value
  4. Return that value or return zero

Below are four functions that will return window width, window height, horizontal scroll bar position and vertical scroll bar position. Although there is fifth function that filters through the values returned by different methods to determine the right one.


function f_clientWidth() {
	return f_filterResults (
		window.innerWidth ? window.innerWidth : 0,
		document.documentElement ? document.documentElement.clientWidth : 0,
		document.body ? document.body.clientWidth : 0
	);
}
function f_clientHeight() {
	return f_filterResults (
		window.innerHeight ? window.innerHeight : 0,
		document.documentElement ? document.documentElement.clientHeight : 0,
		document.body ? document.body.clientHeight : 0
	);
}
function f_scrollLeft() {
	return f_filterResults (
		window.pageXOffset ? window.pageXOffset : 0,
		document.documentElement ? document.documentElement.scrollLeft : 0,
		document.body ? document.body.scrollLeft : 0
	);
}
function f_scrollTop() {
	return f_filterResults (
		window.pageYOffset ? window.pageYOffset : 0,
		document.documentElement ? document.documentElement.scrollTop : 0,
		document.body ? document.body.scrollTop : 0
	);
}
function f_filterResults(n_win, n_docel, n_body) {
	var n_result = n_win ? n_win : 0;
	if (n_docel && (!n_result || (n_result > n_docel)))
		n_result = n_docel;
	return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}

6. Notes

Above code worked with all the browsers I could get ahold of (and usually those are the only ones you should worry about), but there is always the possibility of the problem with some "exotic" browser. I'll be glad to extend the compatibility table with the information provided by the users so you're feedback is very much appreciated. Contact us at the support page.
--

Sincerely tigra.

7. Responses

From Kevin D.:

You have a great idea when you return the lesser value that is greater than zero (to get Window height/width), except that the document value returned can be smaller than the window value (in height), thus not giving the accurate window value.

A better approach might be to use an algorithm like this:

function getWindowHeight() // viewport, not document
{
    var windowHeight = 0;
    if (typeof(window.innerHeight) == 'number')
    {
        // DOM compliant, IE9+
        windowHeight = window.innerHeight;
    }
    else
    {
        // IE6-8 workaround, Note: document can be smaller than window
        var ieStrict = document.documentElement.clientHeight; // w/out DTD gives 0
        var ieQuirks = document.body.clientHeight; // w/DTD gives document height
        windowHeight = (ieStrict > 0) ? ieStrict : ieQuirks;
    }
    return windowHeight;
}

This example is a form of browser sniffing, in that only IE does not return a value for window.innerwidth, however, I am yet to devise a strategy where this is not necessary.

Hope this helps,
Kevin

P.S. This is my collection of similar functions:

function getWindowHeight() // viewport, not document
{
    var windowHeight = 0;
    if (typeof(window.innerHeight) == 'number')
    {
        // DOM compliant, IE9+
        windowHeight = window.innerHeight;
    }
    else
    {
        // IE6-8 workaround, Note: document can be smaller than window
        var ieStrict = document.documentElement.clientHeight; // w/out DTD gives 0
        var ieQuirks = document.body.clientHeight; // w/DTD gives document height
        windowHeight = (ieStrict > 0) ? ieStrict : ieQuirks;
    }
    return windowHeight;
}
 
function getWindowWidth() // viewport, not document
{
    var windowWidth = 0;
    if (typeof(window.innerWidth) == 'number')
    {
        // DOM compliant, IE9+
        windowWidth = window.innerWidth;
    }
    else
    {
        // IE6-8 workaround, Note: document can be smaller than window
        var ieStrict = document.documentElement.clientWidth; // w/out DTD gives 0
        var ieQuirks = document.body.clientWidth; // w/DTD gives document width
        windowWidth = (ieStrict > 0) ? ieStrict : ieQuirks;
    }
    return windowWidth;
}
 
function getScrollTop()
{
    var scrollTop;
    if(typeof(window.pageYOffset) == 'number')
    {
        // DOM compliant, IE9+
        scrollTop = window.pageYOffset;
    }
    else
    {
        // IE6-8 workaround
        if(document.body && document.body.scrollTop)
        {
            // IE quirks mode
            scrollTop = document.body.scrollTop;
        }
        else if(document.documentElement && document.documentElement.scrollTop)
        {
            // IE6+ standards compliant mode
            scrollTop = document.documentElement.scrollTop;
        }
    }
    return scrollTop;
}
 
function getScrollLeft()
{
    var scrollLeft;
    if(typeof(window.pageXOffset) == 'number')
    {
        // DOM compliant, IE9+
        scrollLeft = window.pageXOffset;
    }
    else
    {
        // IE6-8 workaround
        if(document.body && document.body.scrollLeft)
        {
            // IE quirks mode
            scrollLeft = document.body.scrollLeft;
        }
        else if(document.documentElement && document.documentElement.scrollLeft)
        {
            // IE6+ standards compliant mode
            scrollLeft = document.documentElement.scrollLeft;
        }
    }
    return scrollLeft;
}