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:
I've searched the network and found three different methods browsers use to provide the information about window size and scroll bar position:
| Parameter | window | documentElement | body |
|---|---|---|---|
| Window Width | window.innerWidth | document.documentElement.clientWidth | document.body.clientWidth |
| Window Height | window.innerHeight | document.documentElement.clientHeight | document.body.clientHeight |
| Horizontal Scroll | window.pageXOffset | document.documentElement.scrollLeft | document.body.scrollLeft |
| Vertical Scroll | window.pageYOffset | document.documentElement.scrollTop | document.body.scrollTop |
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.
| 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 |
So we have bad news and good news:
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:
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;
}
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.
--
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;
}