ATENÇÃO: Este conteúdo é muito antigo! Foi escrito antes da jQuery. Mas se você quer mesmo aprender Javascript, assista o Workshop de Javascript Avançado.

DHTML Crossbrowser Fácil
Um Guia Rápido para Desenvolvedores

A Guerra dos Browsers Acabou

Se você aprendeu (ou tentou aprender) DHTML há algum tempo, na época da guerra entre Netscape 4 e Internet Explorer 4, tem boas razões para ter calafrios quando ouve falar no assunto. Era o início do DHTML, Microsoft e Netscape tinham criado sua própria versão do modelo de objetos para documentos HTML, o DOM, tornando um inferno a vida do desenvolvedor que pretendia desenvolver um script que funcionasse em ambos os navegadores.

Naquele tempo triste era muito comum que um script construído para um navegador dobrasse de tamanho ao ser adaptado para funcionar em ambos. E as documentações ganhavam volume com uma infinidade de linhas com "Tratamento de evento para o Internet Explorer" ou "Obtendo o Objeto para o Netscape". É claro, isso tinha que ter um fim.

E teve. A Guerra dos Browsers Acabou. E não foi a Microsoft que deu fim ao impasse, foram os padrões web. Tais padrões, criados pelo W3C, nos deram um ambiente comum, bastante semelhante em todos os navegadores atuais. Desde o Internet Explorer, em suas versões mais atuais (5 e 6), até o moderníssimo Opera 7, passando pelos navegadores baseados no engine Gecko: Netscape 6 e 7, Mozilla e Firebird, pelos navegadores dos gerenciadores de janela do Linux, Konkeror (KDE) e Galeon (Gnome), e incluindo o novíssimo Safari da Apple (baseado no engine KHTML, o mesmo do Konqueror) todos os navegadores atuais possuem considerável complacência aos padrões.

Talvez você esteja pensando: "não, a Guerra dos Browsers não terminou. Ainda temos Mozilla e Opera e a guerra é mais atual do que nunca". Eu sei. Você está certo. Mas eu estou chamando de "Guerra dos Browsers" aquele período em que a Microsoft, preocupada com o domínio da Netscape na web, começou a oferecer seu navegador de graça, e embutí-lo no sistema operacional.

DHTML é a interação entre três tecnologias diferentes, HTML, CSS e javascript, através de uma interface conhecida como DOM (Document Object Model). Todos os navegadores hoje possuem boa complacência aos padrões definidos para o HTML e para o javascript, de modo que o desenvolvedor pode ter grande segurança dos resultados. Já em relação ao CSS, a maioria dos navegadores segue fielmente os padrões, exceto um: o Internet Explorer para Windows. Isso pode representar um problema se você precisa usar os recursos mais avançados da linguagem, mas não é nada que algum tempo estudando o assunto não resolva.

Já em relação ao DOM, é preciso entender algo importante. Todos os navegadores hoje oferecem complacência mais que suficiente ao padrão do W3C para o DOM, mas a maioria, em especial a Microsoft, gosta de extender esse DOM adicionando funcionalidades e recursos proprietários. De maneira geral, tudo o que os recursos proprietários oferecem pode ser feito satisfatoriamente com o DOM padrão. Ou seja, você não perde nada em abandonar as extensões proprietárias ao DOM, e ganha significativa compatibilidade. Na prática isso vai trazer-nos apenas uma restrição: não use o material da Microsoft para aprender. Se você aprender as coisas do "jeito Microsoft" seus scripts vão funcionar apenas no Internet Explorer. Se aprender a fazer do "jeito W3C" vai produzir com a mesma eficiência e seus scripts vão funcionar em qualquer navegador. Simples assim.

O Que Você Precisa Saber

Este não é um tutorial de HTML, CSS ou javascript. Vamos falar do DOM, e de como ele é usado para integrar essas três linguagens. Por isso, é importante que você já as conheça, pelo menos superficialmente:

Lembrete: DOM significa Document Object Model. Não é uma linguagem. É uma interface, uma maneira de acessar através de javascript elementos criados pelas linguagens HTML e CSS

O Bom, o Mal e o Sensato

Há um assunto sobre o qual preciso lhe dar algumas idéias antes de prosseguirmos: DHTML não é acessível a quem não tiver um browser moderno, e, dependendo de como você o projetou, a quem não possui as mesmas habilidades que você. Isso pode ter uma série de implicações indesejadas. Um deficiente visual usando um leitor de tela pode não conseguir entrar no site se você não fornecer um substituto para o menu DHTML. O Google pode não conseguir indexar suas páginas se seu robozinho não encontrar um link comum para elas (o que pode acontecer se você, por exemplo, criou um esquema de navegação esotérico com DHTML.)

Por isso ao criar scripts DHTML você precisa analisar seu impacto sobre a acessibilidade e indexabilidade da página. De maneira geral não é complexo adaptar o script ou oferecer alternativas para que o site se mantenha acessível. Mas não há regras aqui, cada caso é um caso e a decisão é sempre sua. De maneira geral é bom construir seus scripts de maneira que seu site ou aplicação continue acessível se eles falharem, e é bom também testar o site em um navegador sem javascript, pois é assim que o Googlebot, por exemplo, vai vê-lo.

Mil maneiras

Existe mais de uma maneira certa de fazer cada coisa, e, é claro, infinitas maneiras erradas. Vou me contentar em explicar aqui apenas uma maneira certa de fazer cada coisa. É mais do que suficiente para trabalhar direito. Se você quiser saber tudo o que está escrito nas especificações, então leia as especificações. Por exemplo, o objeto history contém o método go que recebe um parâmetro numérico para avançar ou retroceder no histórico. Assim, history.go(-1) retrocede e history.go(1) avança. O objeto history tem também o método back para voltar e o forward para avançar. Então, como não pretendo escrever uma referência, vou falar aqui apenas do método go, que te permite fazer tudo. Isso é essencial para que você consiga chegar ao fim da leitura sem dormir.

Localizando

A primeira coisa que precisamos fazer é entender o que é o DOM. Ele na verdade é um padrão de interface para que o javascript (e outras linguagens) possa acessar o HTML e o CSS. Também há uma versão do DOM para XML, mas isto está fora do nosso escopo. É porém conveniente saber que, embora o DOM para XML possua diferenças de sua implementação para HTML, muito do conhecimento obtido aqui será útil se você tiver que trabalhar com XML.

O DOM é portanto uma interface para a estrutura HTML+CSS, com uma representação de cada objeto na estrutura. Vamos a um exemplo. Tomemos o código:

<html>
    <head>
        <title>Título do Documento.</title>
    </head>
    <body>
        <h1>Título</h1>
        <p>Texto, texto, texto, <b>texto</b>...</p>
    </body>
</html>

Ao exibir esse documento o navegador vai criar na memória uma estrutura hierárquica (em formato de árvore) com representações de cada objeto exibido. Essa estrutura inicia-se com o objeto window, que representa a própria janela aberta do navegador. Cada navegador tem pequenas particularidades ao montar os detalhes dessa estrutura, mas de maneira geral podemos nos ater ao que eles tem em comum. O documento acima quando exibido pelo navegador deve gerar a seguinte estrutura:

Essa estrutura está bastante simplificada. Cada um desses elementos tem mais propriedades e métodos. Mas por hora basta que você entenda que os elementos são representados numa árvore como mostrado acima, começando no elemento window.

Vamos a um pequeno exemplo?

A primeira coisa a saber é que o nível default no javascript é o nível do objeto window. Inclusive as funções e variáveis que você cria em seus scripts se tornam filhas do objeto window. Por isso, se uma janela possui uma função chamada calculaDados() e ela abre um popup, podemos chamar, de dentro do popup:

opener.calculaDados()

Opener, você se lembra, refere-se à janela mãe, aquela que abriu o popup. Segundo a mesma lógica, tanto faz escrever:

alert("123")
document.write("456")
open("novo.html")
close()
ou:
window.alert("123")
window.document.write("456")
window.open("novo.html")
window.close()

E se você criou uma função chamada minhaFuncaoTeste(), tanto faz chamá-la com:

minhaFuncaoTeste()
ou
window.minhaFuncaoTeste()

Então, seguindo a árvore do DOM, para chegar até o objeto H1, podemos usar:

document.body.childNodes[0]

Você vai notar que o espaço em branco e a quebra de linha entre a tag body e a tag h1 constituem, no Opera e no Mozilla, um childNode. Por isso o Exemplo 1 traz a tag h1 junto à tag body. Não se preocupe com isso, essa construção é raramente usada e serve apenas como um exemplo.

Usamos document porque o nível default é window, lembra? Poderíamos ter escrito window.document, mas é desnecessário. Veja como usar a linha acima com a propriedade innerHTML para obter o conteúdo da tag h1 no exemplo 1. É um exemplo bem simples e não deve representar nenhuma dificuldade para você.

Na verdade esse é apenas um dos métodos, muito pouco usado, para se acessar um elemento no DOM. Veremos a seguir métodos muito mais práticos e populares.

Rastreamento Via Satélite

Há um método para se acessar elementos do HTML que é de longe o mais usado, e é também o mais simples de todos. Trata-se do método getElementById, disponível no objeto document. Lembra-se de que, ao estudar CSS, você aprendeu que podem haver diversos elementos com a mesma classe, mas só pode haver um elemento de cada id no documento (o quê? Nunca estudou CSS? Você não sabe o que está perdendo.) Pois bem, esse id único serve para que você possa acessar via javascript exatamente aquele elemento. Uma breve olhada no exemplo 2 deve elucidar o assunto.

Aqui está o segredo da simplicidade. O método getElementById está disponível no Internet Explorer desde a versão 5.0. Mas os desenvolvedores, acostumados com a collection proprietária document.all, não se importaram muito com isso. No velho Netscape 4 havia ainda uma outra collection, document.layers. A lei é: esqueça essa baboseira toda e use document.getElementById sempre. Boa parte dos scripts já existentes vão se tornar crossbrowser se você simplesmente fizer isso.

Os Cães Farejadores

Há ainda um outro método para acessar os elementos do HTML que vale a pena conhecer. É o método getElementsByTagName, do objeto document. Esse método recebe uma string com um nome de tag e retorna um Array com todas as tags daquele tipo. Por exemplo:

paragrafos=document.getElementsByTagName("p")

Se você não sabe trabalhar com Arrays, recomendo que tome um tempo para estudar o assunto. É essencial daqui em diante.

Essa linha armazena na variável paragrafos um Array com todas as tags p do documento. Imagine agora que queiramos, por exemplo, colocar uma borda vermelha em todos os parágrafos do documento. Podemos usar:

paragrafos=document.getElementsByTagName("p")

for(var x=0;x<paragrafos.length;x++)
    paragrafos[x].style.border="1px solid red"

Você pode ver esse código funcionando no exemplo 3. Agora vamos complicar um pouco mais. Teremos alguns parágrafos com a classe css "maior". Queremos colocar borda apenas nesses. É na verdade muito simples:

paragrafos=document.getElementsByTagName("p")

for(var x=0;x<paragrafos.length;x++)
    if(paragrafos[x].className=="maior")
        paragrafos[x].style.border="1px solid red"

O atributo className contém o nome da classe CSS do elemento. Assim, com esse comando if, selecionamos apenas os parágrafos cuja classe é "maior". Veja isso funcionando na prática no exemplo 4.

Esse é apenas um exemplo de como usar um atributo para filtrar um Array de elementos. Além do atributo className há uma série de outros que são padrão e podem ser utilizados. Segue a lista dos mais comuns:

A lista acima pode ser usada em praticamente qualquer elemento HTML e cobre quase todas as situações desejáveis num script DHTML. Ou seja, existe muito mais do que isso, mas sabendo apenas essa lista você dificilmente sentirá falta de algo.

Maquiagem

Veja bem, o jeito correto de se construir páginas web hoje é usar o HTML para estruturar seu conteúdo e formatá-lo via CSS. Se você não conhece bem CSS, já está alguns anos atrasado. É hora de correr atrás do prejuízo.

Agora você já sabe como localizar um elemento HTML em seu script. Vamos então mexer com ele agora. E a primeira coisa que vamos aprender a fazer é alterar sua aparência e posicionamento via CSS.

Ascensão Social

Um grande truque para se alterar a aparência de um elemento é modificar seu className. Basta que se criem duas classes CSS com os estilos que se quer alternar entre os elementos. Vamos a um exemplo simples:

Ao usar um handler de evento HTML, como onmouseover ou onclick, o código é executado como método do objeto HTML que recebe o evento. Assim, ao detectar um click num botão, podemos usar a palavra chave this para fazer referência ao botão, ao invés de usar algum método para localizá-lo.

<style type="text/css">
input.normal{
    border:1px solid black;
    background:#FFC;
    color:navy;
}

input.comfoco{
    border:1px solid red;
    background:navy;
    color:#FFC;
}
</style>
 .
 .
 .
<input type="text" class="normal"
 onfocus="this.className='comfoco'"
 onblur="this.className='normal'">

Detalhe interessante: o efeito visual desse exemplo pode ser obtido sem DHTML, usando apenas a pseudo classe :focus do CSS. O problema é que essa pseudo classe não funciona no Internet Explorer. Se você têm algum browser melhor que o Internet Explorer, como o Opera ou o Mozilla Firebird, pode ver como funcionaria o código simplificado no exemplo 6.

Você pode ver esse código funcionando no exemplo 5. Como você pode ver, há muito pouco javascript ali e não será difícil adaptá-lo.

Cirurgia Plástica

Outro meio muito utilizado para se alterar a aparência de um elemento é a propriedade style. Essa propriedade é um objeto da classe CSSStyleDeclaration. Ele contém um membro para cada atributo CSS disponível no navegador.

A regra é muito simples: atributos de nome simples, como padding, mantém seu nome. Assim, para alterar o padding de determinado elemento usamos:

elemento.style.padding="20px"

Já se o atributo desejado é formado por mais de uma palavra, como padding-top, remove-se o hífen e transformando a inicial da segunda palavra em maiúscula. Assim, para alterar o padding-top de um elemento usamos:

elemento.style.paddingTop="50px"

Os valores dos atributos são sempre string. Não se esqueça de passar as unidades de medida. Passar "20" para um atributo pode ter efeitos diferentes em diferentes navegadores (inclusive efeito nenhum). Já "20px" serão 20 pixels em todos.

Como todos os atributos CSS estão disponíveis para alteração individual, esse método proporciona grande flexibilidade, como você pode ver no exemplo 7.

Atacando na Raiz

Talvez você esteja pensando em uma mudança mais radical. Nada de mudar o estilo de um elemento ou outro, você quer mudar o layout do site inteiro. Ok, não há problemas aqui também. Você pode selecionar e alterar diretamente os elementos link e style que fazem referência ao seu CSS.

Para obter uma referência ao elemento desejado você pode usar o método getElementsByTagName do objeto document ou pode colocar um id no elemento e usar o getElementById. Embora pareça haver uma preferência geral pelo primeiro método, eu prefiro o segundo que não me obriga a fazer malabarismos com o Array resultante para encontrar o elemento certo.

Elementos link e style não são elementos comuns do HTML, logo não têm disponíveis todas as propriedades e métodos que você usaria, por exemplo, para alterar um parágrafo de texto. Uma vez encontrado o elemento, há dois métodos para modificá-lo. O primeiro, mais comum, funciona com ambos os tipos de elementos, é a propriedade disabled. Atribuindo o valor true a disabled você desativa a folha de estilos. Atribuindo false você a reativa.

Um belo exemplo de style switcher que utiliza a propriedade disabled pode ser visto no site do médico motoqueiro Diógenes, sabadonaestrada.com.br. E o Diógenes escreveu um excelente tutorial de como fazê-lo.

Um método muito usado para se criar style switchers é colocar várias folhas de estilo em uma página e criar um script para habilitar apenas uma delas. Você pode ver um script de style switcher bem simples no exemplo 8. Acrescente algumas linhas para gravar um cookie com as preferências do usuário e você terá um completíssimo style switcher.

O segundo método não pode ser usado em objetos style, apenas link. Consiste em trocar o atributo href do link, fazendo-o apontar para outro arquivo CSS. Tem como vantagem a simplicidade do script e o fato de o usuário não fazer o download de todas as folhas de estilo se não for usá-las. A desvantagem é que, ao acionar a mudança, o browser ainda vai fazer o download do arquivo CSS, criando um pequeno delay entre o clicar no link e o exibir do layout.

A técnica oferece porém a flexibilidade de trabalhar via script a URL do CSS a ser carregado. Uma pequena demonstração do que pode ser feito você encontra no exemplo 9. Você deve notar que, embora todos os nossos exemplos aqui trabalhem com apenas uma folha de estilo de cada vez, você pode trabalhar com mais de uma folha de estilo habilitada, exibindo uma combinação delas.

Conteúdo

Agora que sabemos como alterar a aparência de um elemento qualquer ou mesmo de toda a página através de DHTML, vamos passar a estudar como alterar o conteúdo de um elemento HTML. Aqui também não é necessário esgotarmos tudo o que há sobre o assunto, uma vez que os dois métodos de que falaremos cobrem a esmagadora maioria das situações.

HTML

O primeiro método que podemos usar para alterar o conteúdo de um elemento é o mais abrangente (e mais usado). Trata-se da propriedade innerHTML, que contém o código HTML interno ao elemento. Você pode lê-la e alterá-la. Assim, para trocar o conteúdo de um elemento qualquer usamos:

elemento.innerHTML="Novo conteúdo."

O valor de innerHTML é código HTML puro. Claro, como você já deve estar imaginando, pode criar novos elementos passando código HTML.

Há um bug no exemplo 10. Experimente, por exemplo, digitar href e clicar OK. O primeiro que me mandar esse código corrigido ganha um pé-de-moleque.

Veja um exemplo da propriedade innerHTML funcionando com um script que usa expressões regulares para ressaltar uma palavra no texto no exemplo 10.

Tabela

Imagine o trabalho que pode gerar criar um script para alterar o conteúdo de uma tabela HTML extensa usando innerHTML. Por conta disso há uma série de métodos específicos para criação e alteração do conteúdo de tabelas. O objeto table por exemplo contém:

O objeto TableRow contém:

O objeto TableCell contém:

É claro, além desses membros especiais, cada um desses elementos ainda conta com as propriedades de todos os elementos html, como className e innerHTML. Você pode ver como usar as propriedades especiais das tabelas no exemplo 11

Comportamento

Agora vamos tratar de algo essencial: responder às ações do usuário. Como você deve saber, uma ação que o usuário execute em qualquer objeto de uma página é chamada de "evento". Há duas maneiras de associar código a um evento. A primeira, mais simples e muito comum, é feita diretamente no HTML, assim:

<input type="button" value="Excluir" onclick="excluir()">

O código acima gera um botão que, quando recebe o evento click (ou seja, quando o botão é pressionado) executa a função javascript excluir().

Você provavelmente já viu código assim um milhão de vezes. É, em muitos aspectos, a maneira mais fácil de se capturar eventos em uma página simples. Há porém dois problemas básicos com o método que nos levam a querer aprender mais:

  1. Falta de flexibilidade: você não pode criar de maneira transparente um script que mude o comportamente de um objeto.
  2. Acoplamento: um dos objetivos dos Web Standards é que você possa manter separados conteúdo e estrutura (HTML), aparência (CSS) e comportamento (javascript). Além de ferir os modernos princípios do webdesign, ainda destrói qualquer encapsulamento, acoplando seu script ao seu HTML.

Talvez você não esteja familiarizado com análise de sistemas. Chamamos de acoplamento a criação de código não organizado, de modo que alterar uma parte simples do código pode exigir alterações em diversas outras partes que usam ou são usadas por aquela. Chamamos, em orientação a objetos, de encapsulamento a propriedade que os objetos possuem de conter todo o código referente ao que eles devem fazer, tornando-os praticamente independentes do lugar onde serão usados e reduzindo drasticamente o acoplamento.

EventListeners

Há, é claro, uma maneira de se atribuir código a um evento em um objeto através de script, o que garante grande flexibilidade e evita o acoplamento. Não se iluda, simplesmente conhecer essa técnica não vai fazer automaticamente com que você desenvolva scripts com bom nível de encapsulamento. É preciso algum estudo e planejamento para isso. Por outro lado, não conhecer estes detalhes vai impedí-lo de realmente separar comportamento de conteúdo.

Vamos então ao que interessa. A recomendação do W3C sugere um método, addEventListener, para se atribuir um handler a um evento qualquer. Isso funciona adequadamente em todos os navegadores atuais, exceto o Internet Explorer. Para este, a Microsoft inventou seu próprio jeitinho de fazer as coisas. Trata-se do método attachEvent, que faz exatamente a mesma coisa do método proposto pelo W3C. Felizmente, é muito fácil criar uma função compatível com todos os navegadores:

function addEvent(obj, evType, fn){
    if (obj.addEventListener)
        obj.addEventListener(evType, fn, true)
    if (obj.attachEvent)
        obj.attachEvent("on"+evType, fn)
}

Essa função é uma simplificação da usada por Simon Willison. Com ela você pode adicionar um handler de evento a um objeto de maneira simples, funcioando em qualquer navegador atual, como mostrado no exemplo 12. No exemplo 13 você pode ver como é simples separar conteúdo de comportamento. Se você remover a tag <script> no começo do documento vai ter HTML puro, sem vestígios de qualquer DHTML.

Tratamento de Eventos

Basta agora entendermos como, numa função que recebe um evento, podemos entender qual foi o evento capturado e em que objeto ocorreu. Chamamos a isso de tratar o evento. Novamente, temos aqui uma diferença entre o que o Internet Explorer inventa e o padrão que o resto do mundo segue. Para qualquer navegador basta colocar um argumento na declaração de função que será handler e esse argumento receberá o evento (um objeto da classe Event). No Internet Explorer o evento atualmente sendo tratado é armazenado em window.event. Assim, na função de tratamento de evento ficamos com:

recebeClick(e){
if (typeof(e)=='undefined')var e=window.event
...
}

Basta isso para que em todos os navegadores a variável e contenha o objeto Event. Agora temos outro problema: a forma de capturar o alvo do evento (o objeto que recebeu o evento) também é diferento no Internet Explorer. Ficaremos com o seguinte:

recebeClick(e){
if(typeof(e)=='undefined')var e=window.event
source=e.target?e.target:e.srcElement
...
}

Agora está quase pronto. Temos ainda uma diferença na implementação, no Safari (olha só, não é o Internet Explorer quem está criando caso desta vez). Se o objeto que recebe o evento contiver texto, não sendo uma imagem ou campo de formulário, o source capturado será o texto dentro do objeto. Um HTMLNodeElement do tipo 3. Não sei quem foi o maluco que implementou isso, mas agora ficamos assim:

recebeClick(e){
if(typeof(e)=='undefined')var e=window.event
source=e.target?e.target:e.srcElement
if(source.nodeType == 3)source = source.parentNode
...
}

Você pode ver esse código funcionando no exemplo 14 (função foi()). Pode também ver algo mais complexo feito com isso no exemplo 15 (função sortThisTable()).

Está certo. Nesse capítulo o código começou a ficar mais complexo e tivemos que alterar um pouco o código para que funcionasse em todos os browsers. Mas faça as contas. Colocamos uma função a mais, addEvent, no começo de cada script, e três linhas a mais em cada handler de evento. Nada realmente complicado.

Window, Document e etc.

Vamos ao feijão com arroz. É preciso entender o que podemos usar dos objetos window e document e de seus membros, para não ter surpresas desagradáveis com algum navegador. Não tenho aqui a pretensão de escrever uma referência a estes objetos. Há ótimas referências na web. Vou apenas mostrar exemplos das tarefas mais comuns em DHTML.

Novas janelas

Para abrir novas janelas do navegador, usamos o método open do objeto window, no formato:

window.open(endereco, nome, parametros)

Onde endereco recebe o endereço da página a ser aberta, nome recebe o nome da nova janela (assim páginas podem ser abertas nela usando o atributo target dos links) e parametros recebe uma lista de características da nova janela.

Se já houver uma janela aberta com o nome especificado, a página é carregada nesta janela. Se nenhum parâmetro for especificado, será aberta uma janela comum do navegador. Se for especificada a altura ou a largura sem nenhum outro parâmetro será aberta uma janela sem barras de ferramentas e barra de status. Os demais parâmetros você pode encontrar em qualquer boa referência de javascript.

Tamanho da tela

Há vários métodos disponíveis para calcular resolução de tela, tamanho da janela atual e do documento. O objeto window possui as propriedades screenX e screenY que retornam a posição da janela atual na tela. Aqui, mais uma vez, o Internet Explorer não segue o padrão, inventando seu próprio jeito de fazer as coisas. Felizmente, é muito fácil resolver o problema:

if(!window.screenX)screenX=screenLeft
if(!window.screenY)screenY=screenTop

O código acima cria as propriedades screenX e screenY no Internet Explorer.

O objeto window ainda possui um membro screen, que possui os métodos width e height. Com isso é muito simples saber a resolução da tela. O que pode acontecer é que o Icq ou algum outro programa esteja reduzindo a área útil da tela. Para obter a área útil disponível para programas usamos os métodos availWidth e availHeight do objeto screen.

Você também pode saber o tamanho da área de documento na janela atual usando os membros offsetWidth e offsetHeight de document.body.

Histórico

Se você leu a introdução, então já leu tudo o que eu tenho a dizer sobre o objeto history. Ele é membro de window e possui o utilíssimo método go, que recebe um parâmetro numérico e faz retroceder ou avançar no histórico. Assim: history.go(-2) equivale a clicar no botão voltar duas vezes.

Location

O objeto location representa a localização atual, a URI em que você está navegando agora. Ele possui, entre outros, um membro href, cujo valor é uma string com a URI atual, e um membro reload, um método que recarrega a página atual.

Veja exemplos de uso dos objetos history e location no exemplo 16.

Timeout e Interval

Nenhum segredo aqui. Se você aprendeu usar setTimeout, setInterval, clearTimeout e clearInterval em alguma referência para qualquer navegador, pode usar sem medo, funciona exatamente do mesmo jeito em todos, como você pode ver no exemplo 17.

Formulários

Formulários são, provavelmente, o assunto que gera mais dúvidas em quem está aprendendo HTML. Não é diferente com javascript e DHTML. Basta porém um pouco de persistência no estudo e logo você estará dominando os métodos e propriedades de cada tipo de campo de formulário.

Encontrando

Nossa primeira tarefa é conseguir referenciar o formulário através de script. Para simplificar as coisas, há entre os diversos métodos de se referenciar um formulário, dois que eu gostaria de recomendar. O primeiro, o simples: document.nomedoformulario. Resolve boa parte das necessidades, uma vez que geralmente o formulário possui um atributo name. Se você quiser acessar um formulário sem name ou deseja trabalhar com todos os formulários da página (com um bloco for por exemplo) pode usar o segundo método. O objeto document conte um Array chamado forms. Veja como usar o Array forms no exemplo 18.

O passo seguinte é encontrar os campos dentro do formulário. Aqui também nenhum segredo. Da mesma forma que com o formulário vou recomendar dois, dos inúmeros métodos possíveis. O primeiro é formulario.nomedocampo, simples e direto como você pode ver no exemplo 19. O outro é usando o Array formulario.elements, como você pode ver no exemplo 20.

Radio, checkbox e select

Raramente vejo alguém com problemas para acessar propriedades e métodos de campos text, password, textarea, button e etc. A grande maioria dos problemas acontece ao acessar objetos select, checkbox e radio. Então vamos atacá-los um por um:

Select

A principal dificuldade de muitos desenvolvedores com o select é o fato de ele não possuir um atributo value. A confusão é aumentada pelo fato de o Internet Explorer, ignorando as especificações, oferecer um atributo value. Assim, muita gente programa objetoselect.value, testa apenas no Internet Explorer, e quando abre o site em outro navegador culpa o navegador pelo seu erro.

O objeto select contém dois membros fundamentais para acessarmos seu valor: o Array options e a propriedade selectedIndex. O Array options contém os options do select e cada um deles possui duas propriedades: value, que contém o valor do atributo HTML value daquele option, e text, que contém o texto da opção. O atributo selectedIndex é um número, o índice da opção selecionada. Assim, para obter o valor de um select, usa-se:

objetoselect.options[objetoselect.selectedIndex].value
Como você pode ver no exemplo 21. Claro, para descobrir os valores selecionados numa caixa de seleção múltipla você precisa varrer o Array de options, como no exemplo 22.

Outro problema de muitos desenvolvedores com selects é como criar novas opções dentro de um select. Para isso os navegadores provêem uma função, Option, constructor de um objeto option, com a seguinte sintaxe:

variavel=new Option(texto,valor)

Você pode ver o constructor Option sendo usado no exemplo 23. Já para remover uma opção de um select basta:

objetoselect.options[indice]=null

Você pode ver esse método sendo usado em uma aplicação bastante popular, no exemplo 24.

Checkbox

Para saber se um checkbox está ou não marcado acessamos sua propriedade checked. Podemos também alterá-la, como mostra o exemplo 25. Se você tiver vários checkboxes com o mesmo nome eles podem ser acessados como um Array, como mostra o exemplo 26.

Radio

Com objetos radio temos uma situação peculiar. Dificilmente alguém achará útil um único objeto radio. Eles sempre aparecem em grupos de objetos com o mesmo nome e podem ser acessados com facilidade como um Array, como mostra o exemplo 27. Cada item do Array possui uma propriedade checked que funciona exatamente como no checkbox.

Erros comuns

Já vi tanta gente cometendo esses erros que resolvi avisar você:

Métodos são métodos, propriedades são propriedades

O código a seguir:

window.location.href("teste.html")

Simplesmente não deveria funcionar em lugar nenhum. Infelizmente, alguns navegadores permitem fazer esse tipo de coisa com alguns métodos e propriedades, tratando um como se fosse outro. Depois, quando você tentar executar seu script num navegador que segue os padrões e ele não funcionar, saiba que a culpa é sua e não do navegador.

Procure uma boa referência

Os métodos apresentados aqui devem suprir grande parte de suas necessidades, mas não todas. Você vai precisar descobrir algum recurso do javascript ou algum membro novo de um elemento DOM. Se você estudar a documentação de qualquer site cuja url termine com microsoft.com corre o sério risco de ver seu script funcionando apenas em um navegador. Procure uma boa fonte para estudar, como os tutoriais do W3Schools de DHTML, Javascript, CSS e HTML

Variáveis de mesmo nome

Procure utilizar a palavra chave var antes de definir suas variáveis de escopo local, assim:

function contador(){
    var contador=0
    . . .
}

Isso vai garantir que sua variável contador não conflite com outra variável de mesmo nome em outra função.

Boas práticas de codificação

Mantenha seu código limpo, indentado e comentado. Coloque nomes nas variáveis que representem de fato sua função. Cuide do encapsulamento do código e tente escrever funções reaproveitáveis. Tenha uma biblioteca organizada de scripts úteis. Isso vai te fazer ganhar mais tempo do que imagina.

Conheça Javascript

A linguagem é sua ferramenta. Saiba manuseá-la bem. Veja este código:

function inverte(){
    objSelect=document.form1.select1
    for(var i=0;i<objSelect.options.length;i++){
        if(objSelect.options[i].selected==true){
            objSelect.options[i].selected=false
        }else{
            objSelect.options[i].selected=true
        }
    }
    objSelect=document.form1.select2
    for(var i=0;i<objSelect.options.length;i++){
        if(objSelect.options[i].selected==true){
            objSelect.options[i].selected=false
        }else{
            objSelect.options[i].selected=true
        }
    }

    ...

    objSelect=document.form1.select20
    for(var i=0;i<objSelect.options.length;i++){
        if(objSelect.options[i].selected==true){
            objSelect.options[i].selected=false
        }else{
            objSelect.options[i].selected=true
        }
    }
}

Não há nada de errado com a lógica, mas é extenso, e difícil de manter. Pode ser trocado, com facilidade, por:

function inverte(){
 var f=document.form1
  for(var sel=0;sel<20;objSel=eval("f.select"+(++sel))
   for(var i=0;i<objSel.options.length;i++)
    objSel.options[i].selected=!objSel.options[i].selected
}

Há muita gente por aí que, por pura falta de intimidade com a linguagem, escreve muito código como o primeiro. Então tome tempo para estudar linguagem, será um tempo bem gasto.

Ajude

Encontrou erros neste documento ou nos exemplos? Gostaria de sugerir uma revisão? Por favor avise-me! Critícas e elogios também são bem vindos, é claro.


Elcio Ferreira / Tableless - Fevereiro de 2004

Este artigo foi impresso de http://elcio.com.br/crossbrowser onde você pode sempre encontrar a versão mais atual. Leia mais sobre desenvolvimento com padrões web em http://www.tableless.com.br

ATENÇÃO: Este conteúdo é muito antigo! Foi escrito antes da jQuery. Mas se você quer mesmo aprender Javascript, assista o Workshop de Javascript Avançado.