Como pegar dimensões da janela e da barra de rolagem por javascript

Isso seria efetivamente fácil, se todos os browsers seguissem o padrão da ECMA… mas não é assim que funciona, e ontem um dos meninos que trabalha aqui comigo estava tentando implementar um código javascript que utiliza tanto a altura útil da tela, como a altura visível da janela (window) e a posição das scrollbars.

Papo vai, papo vem, procura na API da Prototype aqui, da jQuery ali, abre o Google acolá… e me deparo com uma página muito completa, que lista os browsers (inclusive por sistema operacional) e qual é o objeto e atributo que está disponível nele.

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

Ou seja, não existe um método que funcione para todos os browser… :(

Mas, o autor criou uma implementação cross-browser que funciona para calcular todos esses atributos (altura e largura da janela, posição da scrollbar horizontal e vertical). Fica a dica!

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;
}

Segue o link da página, para referência: http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html

Primeira experiência com Prototype

 

Eu conhecia a biblioteca de javascript Prototype apenas por documentação e leitura dos posts que o Chris fez por aqui (um passo a passo bem bacana para iniciar o uso da biblioteca), mas até esta semana nunca utilizado ela de verdade.

Comecei a colocar a mão (ui) em um projeto aqui da MMCafé que utiliza a biblioteca, portanto precisei aprender alguma cois, e resolvi documentar minha experiência inicial.

 

Primeiras impressões sobre Prototype

A biblioteca é bacana, mas aparentemente bem mais complexa que jQuery. Segundo o Chris jQuery tem muitas "funções mágicas", o que pode levar ele a ser menos performático. Não sei, não posso opinar, mas admito que tais funções mágicas facilitam a vida em muitos casos! 😀

Ah, essa complexidade pode ser apenas um medo inicial (sair da zona de conforto, né?), então não quero assustar ninguém. Achei a documentação bem boa, mas senti um pouco de falta de alguns exemplos na documentação – nada que uma googleada e alguns blogs não resolvessem.

 

Seletores

Não vou explicar o funcionamento dos seletores, o post é para expor minha opinião apenas. Para quem conhece jQuery, os seletores funcionam de forma parecida com o jQuery , então achei bastante fácil.

 

Elements, observe, objects

Sofri! Demorei bem mais tempo do que eu esperava para fazer algumas coisas simples, como setar ações onclick em elementos de determinada classe.

Nesta curta experiência o que me fez sofrer mesmo foi a forma como o Prototype trabalha com os eventos. Aos leitores desenvolvedores (ou apenas curiosos e entusiastas) tenho um post saindodo forno que explica e exemplifica meu problema e minha solução para setar a ação em uma classe inteira de links.

 

Resumo da obra

Ainda sou mais jQuery pois já estou familiarizado com seus seletores, manipulação de elementos, etc. Também gosto pelo fato da documentação e os diversos blogs com plugins e exemplos de códigos (mas isso não posso comparar pois não procurei blogs específicos sobre Prototype, eles devem existir).

jQuery logo

 

A Prototype não é o bixo de sete cabeças que eu temia, achei a documentação boa, e acredito que a produtividade poder ser equivalente a do jQuery – para quem já conhece o funcionamento da biblioteca e não precisa ficar consultando a documentação a cada 5 minutos, claro.

Continuo achando jQuery mais interessante, mas depois dessa curta experiência estou vendo a Prototype com melhores olhos. 🙂

 

Recomendo a leitura

Aí vai uma seleção de posts sobre Prototype já publicados por aqui, quase todos do Chris:

 

Comparação entre a jQuery e a Prototype

Usando a Prototype – passo 4 – upload de arquivo sem post na tela

Usando a prototype – passo 8 – seletores CSS

Usando a Prototype – passo 3 – arrays e a classe Enumerable

Criar classes em javascript – usando a Prototype

 

 

E você, o que prefere?
Prototype, jQuery ou outra biblioteca? Quais os motivos?

Compartilhe sua opinião nos comentários 😉

Melhorando o objeto Array do javascript

O Guilherme falou num post passado sobre "javascript mais hard" com o qual ele tem tido que trabalhar mais desde que veio fazer parte da equipe lá na MMCafé… e se tem uma coisa que quem está já mais acostumado com javascript acha ruim é a classe Array. Poucos métodos para muita coisa que se pode fazer, ainda mais quando se compara com essa classe em outras linguagens.

Mas, para isso, há a propriedade (sempre digo isso: não confundam com a biblioteca Prototype) prototype do javascript, que possibilita a manipulação de objetos do javascript em runtime, adicionando métodos, por exemplo.

Não vou me dedicar ao funcionamento dessa propriedade, pois já expliquei aqui, então vamos a alguns métodos adicionados a classe Array que podem ser muito úteis:

Bubble Sorte

Array.prototype.sort=function()
  {
      var tmp;
      for(var i=0;i<this.length;i++)
      {
          for(var j=0;j<this.length;j++)
          {
              if(this[i]<this[j])
              {
                  tmp = this[i];
                  this[i] = this[j];
                  this[j] = tmp;
              }
          }
      }
  };

Clear

Array.prototype.clear=function()
  {
      this.length = 0;
  };

Contains

 

Array.prototype.contains = function (element)
  {
          for (var i = 0; i < this.length; i++)
       {
              if (this[i] == element)
          {
                      return true;
              }
          }
          return false;
  };

Você pode adicionar os métodos que você precisar; tenha certeza que sua produtividade aumentará!

Adicionando propriedades a objetos no Javascript em tempo de execução

Uma capacidade muito explorada no javascript é a possibilidade de se adicionar propriedades a objetos em tempo de execução. Como javascript é uma linguagem interpretada, o desenvolvedor tem a possibilidade de adicionar propriedades (variáveis, funções, etc…) a objetos já instanciados.
Para isso, é usado o objeto prototype que todos objetos do javascript possuem.
Abaixo segue uma função que facilita a escrita; ao invés de criar para cada função a ser adicionada no objeto uma entrada diferente, com ela só é necessário passar como parâmetro o objeto e uma lista de propriedades.

/* função recebe dois parâmetros: o objeto e a lista de métodos que serão adicionados a ele */
    function addMethods(myObject, methods) {
       //percorre a lista de métodos
       for (var property in methods) {
           //adiciona o método ao objeto, dinamicamente
           myObject.prototype[property] = methods[property];
      }
}

Para facilitar, segue um código completo onde no onload da página adiciono ao objeto String (nativo do javascript) duas funções e uma variável:

<html>
<head>
    <title>Adicionar metodos</title>
    <script type="text/javascript">
    /* função recebe dois parâmetros: o objeto e a lista de métodos que serão adicionados a ele */
    function addMethods(myObject, methods) {
        //percorre a lista de métodos
        for (var property in methods) {
            //adiciona o método ao objeto, dinamicamente
            myObject.prototype[property] = methods[property];
        }
    }
    window.onload = function() {
        addMethods(String, {
            func1: function() {alert("Sou a funcao 1");},
            func2: function() {alert("Sou a funcao 2");},
            const1: 2
        });
        var myString = new String();
        myString.func1();
        alert(myString.const1);
    }
    </script>
   
</head>
<body>
  
</body>
</html>

Usando funções de callback em javascript para sucesso ou erro

Em mais um post sobre funções como parâmetro de outras funções no javascript, vou mostrar como fazer para passar uma série de funções para determinados fins – como em caso de sucesso, em caso de falha, etc…

Onde sempre vemos isso? Frameworks como jQuery e Prototype dão a opção de você escolher uma função de callback para sucesso de um evento, outra para falha, outra para quando se inicia o drag de um elemento na tela, e por aí vai. Isso é feito passando, ao invés de uma função, uma lista de funções. No caso, para poder dar nome a elas, usamos a estrutura de chave/valor (key/value).

O exemplo abaixo explica como a função wrapper() passa 2 parâmetros e duas funções de callback: uma para sucesso, outra para falha. Na primeira chamada, a função de callback chamada será a de sucesso, pois é possível efetuar a rotina. No outro caso, dará erro, então será usada a função de falha.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script type="text/javascript">
function wrapper(par1, par2) {
    //verifica se o último parâmetro é uma função para chamá-la;
    //os demais parámetros são repassados para a função

    var lastArgument = arguments[arguments.length-1];
   
    try {
        //Classe Math possue o método max(), que retorna o maior valor entre dois parâmetros
        var sum = Math.max(par1, par2);
        //se não for erro, joga (throw) erro
        if(isNaN(sum))
            throw "not a valid result";
        //se chegar até aqui, é um resultado válido e chama função de sucesso
        lastArgument.onSuccess.call(this, sum);
    } catch(e) {
        //se não, chama a função de erro
        lastArgument.onFailure.call(this, e);
    }
       
   
}

function errorHandler(e) {
    alert("Error: " + e);
}

function myFunction(sum) {
    alert(sum);
}
wrapper(10, 2, {onSuccess: myFunction, onFailure:errorHandler}); //será chamado sucesso
wrapper(10, "a", {onSuccess: myFunction, onFailure:errorHandler}); //será chamado erro
</script>
</head>
<body>

</body>
</html>