Dimensiones del viewport y de la página

La información mostrada en esta página ha sido verificada en Firefox 3.0.1, IE7 y Safari 3.1.2

Antes de empezar: document.compatMode

La propiedad compatMode de document nos permite saber si estamos trabajando en el modo "Quirks" o en el modo "Strict". Dicha propiedad puede tomar los siguientes toma los siguientes valores:
En "strict mode" En "quirks mode"
document.compatMode CSS1Compat BackCompat
Las referencias a esta propiedad pueden encontrarse en los siguientes enlaces:
http://developer.mozilla.org/En/DOM/Document.compatMode
http://www.opera.com/docs/specs/doctype
http://msdn.microsoft.com/en-us/library/ms533687(VS.85).aspx
Más documentación sobre los modos puede encontrarse en:
http://www.communitymx.com/content/article.cfm?cid=85FEE
http://www.ericmeyeroncss.com/bonus/render-mode.html

ViewportOut

En Firefox, Opera y Safari puede determinarse mediante:
    window.innerWidth
    window.innerHeight
Para IE7 en modo no estricto
    document.documentElement.scrollWidth
    document.documentElement.scrollHeight

    document.documentElement.offsetWidth
    document.documentElement.offsetHeight

    document.body.offsetWidth
    document.body.offsetHeight   
Para IE7 en modo estricto no hay posibilidad directa.

Función

  if (window.innerWidth !== undefined) {//ViewportOut
    window.viewportOutSize = function(){return {w:window.innerWidth,h:window.innerHeight};}
  }
  else if (!compat) {//ViewportOut para IE7-Q 
    window.viewportOutSize = function(){return {w:html.scrollWidth,h:html.scrollHeight};}    
  }
  else {//IE7-S : No tenemos ViewportOut (¿nos conformamos con ViewportIn?)
    return null;
  }

ViewportIn

En modo estricto

En Firefox, Safari y IE7
    document.documentElement.clientWidth
    document.documentElement.clientHeight   
En Opera no es posible directamente:
    document.documentElement.clientWidth
    document.documentElement.clientHeight  

        Nos proporcionan las dimensiones del panel virtual (scroll panel)

    document.body.clientWidth
    document.body.clientHeight  

        Nos proporcionan las dimensiones del ViewportOut - 2*(body.margin+body.borderWidth)
 

En modo no estricto

En Firefox y Safari
    document.body.clientWidth
    document.body.clientHeight   
En IE7: no directamente
    document.body.clientWidth
    document.body.clientHeight   

        Nos proporcionan las dimensiones del ViewportOut - 2*(body.borderWidth)
En Opera no es posible directamente:
    document.documentElement.clientWidth
    document.documentElement.clientHeight  

        Nos proporcionan ?????

    document.body.clientWidth
    document.body.clientHeight  

        Nos proporcionan las dimensiones del ViewportOut - 2*(body.margin+body.borderWidth)
 

Función

Supongamos que
  var compat = (document.compatMode == 'CSS1Compat');
  var html   = document.documentElement;
  var body   = document.body;
  
  if (compat) {
    window.viewportInSize = function(){return {w:html.clientWidth,h:html.innerHeight};}
  }
  else {
    window.viewportInSize = function(){return {w:body.clientWidth,h:body.clientHeight};}
  }
 
Tendriamos los siguientes errores
Opera-Strict:
        Nos proporciona las dimensiones del ScrollPane
Opera-Quirks:
        Nos proporciona las dimensiones del ViewportOut - 2*(body.margin+body.borderWidth)
IE7-Quirks
        Nos proporciona las dimensiones del ViewportOut - 2*(body.borderWidth)

Panel virtual (ScrollPane)

Llamaremos tamaño de la página a: body.margin+body.border+body.width

Tamaño de la página mayor que el viewportOut

En todos los navegadores y modos, exceptuando IE7 en modo no estricto:
    document.documentElement.scrollWidth
    document.documentElement.scrollHeight
El pagesize efectivo vendrá dado por
    Math.max(document.documentElement.scrollWidth,viewportIn.width)
Sabemos que la implementación simple de viewportIn daría errores en Opera. Veamos como quedaría:
Opera-Strict:
        viewportIn simple nos proporciona las dimensiones del ScrollPane, 
        en consecuencia seguiriamos con el valor de scrollpane
Opera-Quirks:
        viewportIn simple Nos proporciona las dimensiones del 
        ViewportOut - 2*(body.margin+body.borderWidth)
Parece que la opción más simple es utilizar el viewportOut

En IE7 en modo no estricto
    document.documentElement.scrollWidth
    document.documentElement.scrollHeight 

nos proporcionan el viewportOut
Las dimensiones del body que se hayan especificado son despreciadas.

Se tiene una medida del body height computado mediante el body.scrollHeight

Por tanto para IE7-Q el pagesize efectivo vendrá dado por
    Math.max(document.body.scrollWidth,viewportIn.width);
Tendiendo en cuenta que para IE7-Q
  viewportIn.width == document.documentElement.scrollWidth
Llegamos a
    Math.max(document.body.scrollWidth,document.documentElement.scrollWidth);

Módulo de cálculo 1

(function(){
  
  var compat = (document.compatMode == 'CSS1Compat');
  var html = document.documentElement;
  var body = document.body;
  
  if (window.innerWidth !== undefined) {//ViewportOut
    window.viewportSize = function(){return {w:window.innerWidth,h:window.innerHeight};}
  }
  else if (!compat) {//ViewportOut para IE7-Q 
    window.viewportSize = function(){return {w:html.scrollWidth,h:html.scrollHeight};}    
  }
  else {//IE7-S : No tenemos ViewportOut, nos conformamos con ViewportIn
    window.viewportSize = function(){ return {w:html.clientWidth,h:html.clientHeight};}
  }


  if (compat) {
    window.pageSize = function(){
      var vps = viewportSize();
      return {
        w: Math.max(html.scrollWidth,vps.w);
        h: Math.max(html.scrollHeight,vps.h);
      };
    }
  }
  else {
    window.pageSize = function(){
      return {
        w: Math.max(html.scrollWidth, body.scrollWidth);
        h: Math.max(html.scrollHeight,body.scrollHeight);
      }
   }
  }
})();

Página menor que el viewport

En este caso el tamaño efectivo de página será el del viewport

Modulo 2

(function(){
  
  ...

  if (compat) {
    window.pageSize = function(){
      var vps = viewportSize();
      return {
        w: Math.max(html.scrollWidth,vps.w);
        h: Math.max(html.scrollHeight,vps.h);
      };
    }
  }
  else {
    window.pageSize = function(){
      return {
        w: Math.max(html.scrollWidth, body.scrollWidth);
        h: Math.max(html.scrollHeight,body.scrollHeight);
      }
   }
  }
})();

Posición de los cursores de las barras de desplazamiento

Determinar la posición de los cursores de las barras de desplazamiento (es decir, la posición del viewport respecto a la página) es un poco más complicado "por culpa" de Safari.
En Firefox, Opera e IE la posición viene dada por scrollLeft y scrollTop como propiedades de document.documentElement en modo estricto y sobre document.body en caso contrario. Pero desgraciadamente en Safari (al menos en las versiones probadas) document.documentElement.scrollLeft y document.documentElement.scrollTop valen cero.
Existe otras propiedades que nos proporcionan la información que estamos buscando: window.pageXOffset y window.pageYOffset, pero desgraciadamente dichas propiedades no existen en IE. Tendremos que preparar una solución para este problema.

El módulo completo

Para optimizar el rendimiento "memoizaremos" un poco:
(function(){
  var ve = (document.compatMode == 'CSS1Compat') ? 
                   document.documentElement: document.body;

  window.viewportSize = function(){ return {w:ve.clientWidth,h:ve.clientHeight};}
  window.pageSize     = function(){ return {w:ve.scrollWidth,h:ve.scrollHeight};}

  window.scrollPosition = 
               (window.pageXOffset !== undefined) ? 
                    function(){return {x:window.pageXOffset, y:window.pageYOffset};}:
                    function(){return {x:ve.scrollLeft, y:ve.scrollTop};};

})();

Un pequeño y típico ejemplo

Pulsando sobre aquí podremos ver un par de rectángulos centrados en el viewport. El código que se ejecuta es el siguiente:
function accion() {
  var vs = viewportSize();
  makeElement('DIV',
              {
                backgroundColor:'#ffe0e0',
                position:'fixed',
                width:'100px',
                height:'40px',
                left:((vs.w-100)/2)+'px',
                top:((vs.h-40)/2)+'px'
               },
               document.body
  );

  var sp = scrollPosition();
  makeElement('DIV',
              {
                backgroundColor:'#e0e0e0',
                position:'absolute',
                width:'80px',
                height:'30px',
                left:((vs.w-80)/2)+'px',
                top:(sp.y+(vs.h-30)/2)+'px'
               },
               document.body
  );
}