DOM e Javascript

O DOM (Document Object Model) é uma interface independente de plataforma e linguagem que permite aos programas e scripts atualizarem dinamicamente a estrutura, conteúdo e estilo de documentos XML, HTML e demais. Isso significa que o DOM não é apenas utilizado em Javascript, de fato, outras linguagens de programação podem fazer uso do DOM em suas funções (Para este artigo em específico, vamos focar apenas em Javascript).

A primeira versão do DOM (DOM Nível 1) foi lançada em 1998 pela W3C, a segunda (DOM Nível 2) em 2000, e, por fim, a terceira versão (DOM nível 3) foi lançada em 2004.

Atualmente podemos trabalhar com 3 partes relevantes de APIs (Application Programming Interface) do DOM: A DOM Core API, que é o núcleo do DOM e fornece tudo o que você precisa para criar, remover e alterar elementos da árvore do DOM de maneira genérica. A DOM HTML API, que é uma parte do DOM voltada especificamente para HTML e foi criada para trazer muitas implementações do BOM (Browser Object Model) que existiam em diversos navegadores antes da padronização. Ela é mais fácil para ser utilizada, pois, tem os mesmos atributos dos elementos HTML (você vai ver posteriormente neste artigo). E, por fim, a DOM XML API, que é voltada para arquivos XML e não cabe no escopo desse artigo, falaremos apenas das duas primeiras.

Aqui você vai encontrar partes extremamente semelhantes (se não iguais) ao que já vimos anteriormente sobre o BOM. Isso acontece porque o BOM é uma implementação do navegador em si, o que significa que cada empresa que cria um navegador pode implementar o BOM como quiser, já o DOM é uma API que foi especificada pela W3C, uma tentativa de padronização para todos os navegadores.

Ainda que alguns dos navegadores não sigam tudo à risca, atualmente é bem mais simples trabalhar com Javascript (ECMAscript) do que já foi um dia.

Ao invés de entrar em muitos detalhes sobre a história do DOM e detalhes que não apresentam o código em si, quero mostrar a você como utilizar o DOM HTML e o DOM CORE para manipular o HTML da página, porém, sei que muitas pessoas se interessam em saber como tudo surgiu para ter um background sobre o que irão utilizar em suas páginas.

Para essas pessoas, encontrei vários links que detalham praticamente tudo o que vão precisar saber (até mais do que isso, para falar a verdade).

A maioria dos links acima está em inglês, mas se você deseja se tornar um programador de sucesso, terá que aprender inglês também.

No artigo tenha o DOM de Alysson Franklin no Tableless, você também vai encontrar vários tópicos que vão ajudar no seu conhecimento sobre o Document Object Model.

O DOM e Javascript

Encontrei, também, um trecho muito interessante no site da MDN (Mozilla Developer Network) e vou traduzi-lo abaixo para mostrar a relação entre o DOM e Javascript:

DOM e JavaScript

O pequeno exemplo mostrado anteriormente neste artigo, como quase todos os exemplos dessa página, foi criado em JavaScript. Ou seja , ele é escrito em JavaScript, mas usa o DOM para acessar o documento e seus elementos. O DOM não é uma linguagem de programação, mas sem ele, a linguagem JavaScript não teria nenhum modelo ou noção de páginas web, páginas XML e elementos com os quais ela geralmente lida. Cada elemento de um documento – o document como um todo, o cabeçalho, as tabelas dentro do documento, cabeçalhos de tabelas, texto dentro da células – faz parte do Document Object Model, para que possam ser acessados ​​e manipulados usando o DOM e uma linguagem de script, como JavaScript.

No início, JavaScript e o DOM eram fortemente ligados , mas eventualmente evoluíram em entidades separadas. O conteúdo da página é armazenado no DOM e pode ser acessado e manipulado via JavaScript, de modo que podemos escrever esta equação aproximada :

API (página web ou XML) = DOM + JS (linguagem de script)

O DOM foi projetado para ser independente de qualquer linguagem de programação particular, tornando a representação estrutural do documento para uma única API consistente. Apesar de se concentrar exclusivamente em JavaScript nesta documentação de referência, implementações do DOM podem ser construídas para qualquer linguagem de programação.

DOM, Javascript e a página como uma árvore

Se você já tem o costume de trabalhar com HTML, provavelmente não terá nenhuma dificuldade em entender como o DOM cria sua árvore.

Perceba o seguinte trecho HTML simples:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		
		<title>Demo</title>
		<script src="meu_arquivo_javascript.js"></script>
	</head>

	<body>
		<h1 id="id_h1" class="classe_h1">Sou um cabeçalho!</h1>
		<p id="id_p" class="classe_p">
			Um texto qualquer dentro de uma tag de parágrafo. Aqui também
			temos outras tags, como <a href="#">um link<a>, ou um texto
			<b>em negrito</b>.
		</p>
		<p id="id_p" class="classe_p">
			Este é outro parágrafo.
		</p>
	</body>
</html>

Agora veja como o DOM cria sua árvore a partir do HTML acima:

Árvore do DOM

Existem elementos pai (parent), filhos (childs) e irmãos (siblings). Estes elementos são caracterizados na forma como estão na árvore, veja o mesmo exemplo na imagem abaixo:

A árvore do DOM

Como você pode perceber, cada elemento tem seus pais e filhos (se aplicável).

É necessário que você entenda essa árvore para que saiba como navegar entre seus elementos utilizando Javascript posteriormente neste artigo.

Elementos pais são tags que têm outros elementos dentro de si. Por exemplo: um <p> que tem um link (<a>) dentro de si, é pai deste link. Por outro lado, o link <a> que está dentro de <p> é filho desse <p>.

<p>Sou o pai, e <a href="#">eu o filho</a></p>

Se um elemento tem mais de uma tag dentro de si, esses elementos são irmãos. Por exemplo, se uma <div> tem dois elementos <p> dentro de si, ambos os elementos <p> são irmãos.

<div>
	<p>Eu sou filho da DIV.</p>
	<p>Eu sou filho da DIV também, e irmão do feioso aí acima.</p>
	
	<h1>Sou um cabeçalho, filho da DIV e irmão dos dois aí acima.</p>
</div>

Localizando elementos (nós) na página

Já vimos algumas maneiras sobre como localizar elementos (também utilizamos o termo nós) na página HTML utilizando Javascript anteriormente, contudo, vamos aprofundar um pouco mais sobre isso abaixo.

Existem milhares de maneiras diferentes de localizar um elemento na página. Dentre elas, temos as seguintes:

getElementById

Com este método, você utiliza o atributo id do nó que está localizando entre parênteses e aspas ao final do método (sem cerquilha #, utilizada em CSS). Como o atributo id deve ser único em uma página HTML (não pode existir mais de um elemento com o mesmo id), o elemento que você estiver buscando será retornado diretamente.

Por exemplo, suponhamos que você queira localizar um nó de parágrafo na sua página HTML:

<p id="id_p" class="classe_p">Um texto qualquer</p>

O parágrafo acima tem o id = id_p, se eu quiser localizar este parágrafo utilizando seu atributo id em Javascript, eu faria o seguinte:

var p = document.getElementById('id_p');

Se você utilizar a caixa de diálogos "alert" para exibir o conteúdo da variável p (criada no trecho acima), terá um HTMLParagraphElement, ou seja, é um único nó de parágrafo em HTML. Isto é simplesmente uma interface do DOM HTML que permite que você obtenha acesso rápido às propriedades e métodos do objeto. 

Com o nó "em mão", você pode alterar, criar ou remover qualquer outro elemento ou atributo baseado no parágrafo (ou qualquer outro elemento) que você está manipulando.

HTMLParagraphElement

Eu poderia, por exemplo, acessar a propriedade backgroundColor do objeto style do elemento p (que acabei de localizar) para definir sua cor de fundo.

// Captura o evento load da página
window.onload=function(){
	// Localiza o elemento com id "id_p"
	var p = document.getElementById('id_p');
	
	// configura a propriedade backgroundColor do elemento
	p.style.backgroundColor='#0000FF';
}

Observação: Perceba que você só poderá trabalhar com elementos após o carregamento da página, por este motivo, tudo o que vamos indicar neste artigo vem dentro do evento "load" da página (veja Eventos em Javascript).

O pequeno trecho de código acima, altera o elemento assim que a página é carregada, então meu <p>:

<p id="id_p" class="classe_p">Um texto qualquer</p>

Se torna isso:

<p class="classe_p" id="id_p" style="background-color: #0000FF;">Um texto qualquer</p>

Um parágrafo com fundo azul.

Além de tudo isso, você também pode localizar filhos de um nó em específico. Considere o seguinte trecho HTML:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		
		<title>Demo</title>
		<script src="meu_arquivo_javascript.js"></script>
	</head>

	<body>
		<h1 id="id_h1" class="classe_h1">Sou um cabeçalho!</h1>
		<p id="id_p" class="classe_p">
			Um texto qualquer dentro de uma tag de parágrafo. Aqui também
			temos outras tags, como <a href="#">um link</a>, ou um texto
			<b>em negrito</b>.
		</p>
		<p id="id_p2" class="classe_p2">
			Este é outro parágrafo.
		</p>
	</body>
</html>

Suponhamos que eu queira localizar os elementos a dentro do primeiro elemento p da página:

window.onload=function(){
	// Localiza o elemento com id id_p
	var p = document.getElementById('id_p');
	
	// Localiza os elementos a (links) dentro do p
	var links = p.getElementsByTagName('a');
	
	// Alerta o atributo href do primeiro link
	alert(links[0].href);
}

Perceba que em minha segunda busca, ao invés de document, utilizei a variável p (que sustenta o nó do parágrafo) para selecionar somente o que está dentro dele.

getElementsByTagName

Você também pode localizar um elemento utilizando o nome da tag que deseja. Para parágrafos, as tags se chamam-se p, DIVs se tem o nome div, para cabeçalhos h1, as tags se chamam h1 (e assim por diante):

var p = document.getElementsByTagName('p');

A diferença aqui é que, ao invés de retornar a interface HTMLParagraphElement do nó, a interface HTMLCollection é retornada.

Isso significa que não temos um elemento diretamente, mas uma coleção de elementos em um array.

Os elementos são gravados no índice do array do mesmo modo em que são encontrados na página (do topo para baixo, da direita para esquerda no seu código HTML), começando pelo índice zero (0). Ou seja, se eu quero acessar o primeiro parágrafo (mesmo que eu tenha apenas um parágrafo na página), começo pelo índice 0 (em seguida 1, 2, 3…).

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos p
	var p = document.getElementsByTagName('p');
	
	// Altera o background do primeiro elemento da coleção [0]
	p[0].style.backgroundColor = 'red';
}

Você também pode utilizar a opção item(N), onde N representa o número do índice:

p.item(0).style.backgroundColor = 'red';

Depois de obter o item(0) ou [0], eu posso continuar trabalhando dentro dele, pois estarei dentro da interface HTMLParagraphElement novamente. Se este objeto tiver filhos, posso utilizar uma referência para chamar seus filhos da seguinte maneira:

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos p
	var p = document.getElementsByTagName('p');
	
	// Localiza os filhos do primeiro elemento encontrado
	var filho = p[0].getElementsByTagName('qualquer_tag_filha');
	
	// Altera o background do primeiro elemento da coleção HTML do filho
	filho[0].style.backgroundColor='red';
}

getElementsByClassName

Este método é praticamente a mesma coisa do getElementsByTagName, ou seja, retorna uma coleção de elementos para um array. O problema aqui é que não funciona com os Internet Explorers mais antigos.

Quando digo antigos, estou me referindo a versões anteriores ao Internet Explorer 9. Isso é preocupante, porque o Internet Explorer 8 ainda é amplamente utilizado.

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos com a classe_p
	var p = document.getElementsByClassName('classe_p');
	
	// Altera o background do primeiro elemento da coleção [0]
	p.item(0).style.backgroundColor = 'red';
}

getElementsByName

Também a mesma coisa, retorna uma coleção HTML de elementos, porém, agora utilizando o atributo name dos mesmos.

Como a tag p não suporta o atributo name, vamos utilizar uma imagem agora:

<img src="#" name="imagem">

No script a ideia é a mesma:

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos com o nome imagem
	var img = document.getElementsByName('imagem');
	
	// Altera o src do primeiro elemento da coleção [0]
	img.item(0).src = 'http://bit.ly/1udSyQN';
}

Porém, agora, ao invés de alterar a cor de fundo, alterei o atributo src da imagem.

O HTML gerado após a alteração do elemento foi:

<img name="imagem" src="http://bit.ly/1udSyQN">

Se você tiver vários elementos (imagens, por exemplo), dentro de uma coleção HTML, um laço cairia muito bem para fazer qualquer alteração nos dados como um todo:

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos com o nome imagem
	var img = document.getElementsByName('imagem');
	
	// Laço para percorrer as imagens
	for ( var i = 0; i < img.length; i++ ) {
		// Altera a url de todas as imagens
		img[i].src = 'nova_url';
	}
}

querySelector e querySelectorAll

Ambos os métodos acima só funcionam com navegadores mais novos, o que também quer dizer que você deve se preocupar, o IE8 também não suporta este método.

Vamos modificar nossa imagem para:

<img src="#" id="id_imagem" class="classe_imagem">

Agora ela tem atributos id e class.

querySelector retorna um único elemento baseado em seu seletor CSS do id do elemento (com cerquilha #).

// Captura o evento load da página
window.onload=function(){
	// Localiza um elemento com o id #id_imagem
	var img = document.querySelector('#id_imagem');
	
	// Altera o src do elemento
	img.src = 'http://bit.ly/1udSyQN';
}

querySelectorAll retorna uma coleção HTML com todos os elementos de uma determinada classe. Também é baseado em seletores de CSS.

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos com a classe .classe_imagem
	var img = document.querySelectorAll('.classe_imagem');
	
	// Altera o src do primeiro elemento da coleção [0]
	img[0].src = 'http://bit.ly/1udSyQN';
}

Dica sobre localização de elementos

Pode ser que você não saiba o que qualquer um dos métodos acima irá retornar, mas é bastante simples descobrir.

Para isso, basta criar uma variável com o elemento desejado e em seguida utilizar a caixa de diálogos alert para ver seu retorno:

// Captura o evento load da página
window.onload=function(){
	// Localiza os elementos com a tag img
	var img = document.getElementsByTagName('img');
	
	alert(img);
}

Se retornar HTMLCollection, é um array de elementos HTML. Você deve utilizar um laço, ou indicar seu índice entre colchetes [N] ou com o método item(N). N representa o número do elemento no índice iniciando do 0 (zero).

// Captura o evento load da página
window.onload=function(){
	// Localiza o elemento com id id_imagem
	var img = document.getElementById('id_imagem');
	
	alert(img);
}

Se retornar algo como HTMLImageElement, ou qualquer outra interface de elemento HTML, você já tem seu elemento. Tendo o elemento em uma variável, você pode "brincar" com ele como preferir.

Veja todos os métodos e propriedades suportados por elementos HTML em HTML DOM Element. Teste várias propriedades e veja o que acontece na página.

DOM e Javascript: Manipulação do HTML

Agora que você já sabe basicamente tudo o que precisa para localizar um elemento em uma página HTML utilizando o DOM e Javascript, vamos ver um pouco mais sobre como alterar algumas coisas dentro desse elemento.

O DOM (juntando todos os nível e APIs voltadas para HTML) tem interfaces especiais que mapeiam todos os elementos de um documento HTML. Ele define os elementos HTML como objetos, as propriedades de todos os elementos HTML, os métodos para acessar todos os elementos HTML e os eventos de todos os elementos HTML.

Em outras palavras: O DOM é (o cara) um padrão para obter, alterar, adicionar ou excluir elementos HTML de uma página.

Todos os elementos do DOM são definidos como objetos, ou seja, eles podem ter métodos e propriedades próprias ou herdadas de outros objetos.

Uma propriedade de um objeto é uma opção que você pode obter um valor (get) ou configurar um valor (set) diretamente.

// Captura o evento load da página
window.onload=function(){
	// Seleciona a div do documento
	var a_div = document.getElementById('a-div');
	
	// Altera o valor do primeiro nó de a-div (set)
	a_div.childNodes[0].nodeValue='Mamma mia!';
}

Um método de um objeto trata-se de uma ação que pode ser feita na página, como adicionar ou apagar um elemento (veremos isso adiante no artigo).

Veja abaixo um exemplo de um método e uma propriedade trabalhando em conjunto:

// Captura o evento load da página
window.onload=function(){
	// Método para localizar um objeto
	var p = document.getElementsByTagName('p');
	
	// Alterando a propriedade innerHTML 
	p.item(0).innerHTML = 'Todo o HTML de p será substituído!';
}

No exemplo acima, utilizei o método getElementsByTagName (índice 0) para localizar o parágrafo que eu queria, em seguida, alterei a propriedade innerHTML (set) desse p, trocando todo o HTML dentro dele pelo valor que eu enviei.

O document

Sempre que você estiver trabalhando com um documento HTML utilizando Javascript, todos os elementos estarão dentro do elemento pai document, ele é o elemento (nó) mais importante da página, e você sempre vai ter que passar por ele para chegar a qualquer outro elemento. Veja um exemplo:

// Captura o evento load da página
window.onload=function(){
	// Método para localizar um objeto
	var p = document.getElementsByTagName('p'); 
	var a = p.item(0).getElementsByTagName('a');
	
	// Agora consegui uma coleção de links dentro do p
	// Mas primeiro tive que passar pelo 
	// document e qualquer método para localizar elementos
	alert(a);
}

Nós do DOM

Tudo é um para o DOM, desde um texto simples dentro de um parágrafo, até o documento inteiro, representado por document. Tudo isso é caracterizado como um nó (Node).

Para navegar por entre os nós do DOM, você pode utilizar as palavras relativas aos nomes de membros de uma família em inglês (ou os métodos que já falamos anteriormente, depende da aplicação), por exemplo:

  • parentNode – Quem é o elemento pai
  • childNodes – Quem são os elementos filhos
  • firstChild – Qual é o primeiro filho
  • lastChild – O último filho
  • nextSibling – O próximo irmão
  • previousSibling – O irmão anterior

No código isso ficaria assim:

// Captura o evento load da página
window.onload=function(){
	// Método para localizar os parágrafos de todo o documento
	var p = document.getElementsByTagName('p');
	
	// Método pega localizar os elementos a dentro do primeiro parágrafo
	var a = p.item(0).getElementsByTagName('a');
	
	// Obtém o texto do primeiro elemento a
	var texto = a[0].childNodes[0].nodeValue;
	
	// Alerta o texto
	alert(texto);
}

Ou, se você quiser obter somente o texto mesmo, poderia utilizar innerHTML (que retorna – get – ou altera – set – todo o HTML dentro do elemento).

// Captura o evento load da página
window.onload=function(){
	// Método para localizar os parágrafos de todo o documento
	var p = document.getElementsByTagName('p');
	
	// Método pega localizar os elementos a dentro do primeiro parágrafo
	var a = p.item(0).getElementsByTagName('a');
	
	// O texto de a
	var texto = a[0].innerHTML;
	
	// Alerta o texto
	alert(texto);
}

Criando nós

Você também pode criar, alterar e apagar seus próprios nós (elementos) de dentro do nó que preferir, veja um exemplo:

O HTML

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		
		<title>Demo</title>
		<script src="meu_arquivo_javascript.js"></script>
	</head>

	<body>
                <!-- Um nó <div> -->
		<div id="a-div"></div>
	</body>
</html>

O Javascript:

// Captura o evento load da página
window.onload=function(){
	// Seleciona a div do documento
	var a_div = document.getElementById('a-div');
	
	// Cria um novo nó <p>
	var novo_p = document.createElement('p');
	
	// Altera a cor de fundo do novo <p>
	novo_p.style.background = 'red';
	
	// Cria um novo nó de texto
	var novo_texto = document.createTextNode('Este é o texto do p!');

	// Adiciona o nó de texto no parágrafo
	novo_p.appendChild(novo_texto);
	
	// Adiciona o nó <p> com seu texto na nossa div
	a_div.appendChild(novo_p);
	
	// Conta 5 segundos e remove o novo elemento
	setTimeout(function(){
		// Remove o nó <p>
		a_div.removeChild(novo_p);
	}, 5000);
}

Para criar um nó, utilize createElement, para criar um novo nó de texto, utilize createTextNode, para adicionar um elemento (nó) dentro do outro, utilize appendChild e para remover um elemento do documento, utilize removeChild.

Alterando o estilo (style) dos elementos:

Para alterar qualquer propriedade do CSS do seu elemento, basta utilizar o objeto style, exemplo:

// Captura o evento load da página
window.onload=function(){
	// Seleciona a div do documento
	var a_div = document.getElementById('a-div');
	
	// Altera a largura da DIV
	a_div.style.width="500px";
	
	// Altera a altura da DIV
	a_div.style.height="500px";
	
	// Altera a cor de fundo da DIV
	a_div.style.background="red";
	
}

Depois do nome do elemento, um ponto e a palavra style, você pode colocar mais um ponto e adicionar a propriedade que deseja alterar. Aqui existe uma lista para você brincar aí no seu código.

Concluindo

O DOM é extremamente grande para conseguirmos cobrir todo o assunto em apenas um artigo (creio que nem 100 o fariam), devem existir até livros inteiros para descrever apenas uma parte do DOM completo (não procurei para saber, perdão).

Tentei passar algumas funções de maneira mais genérica para você entender o que é, e como funcionam o DOM e Javascript trabalhando em conjunto, também passei milhares de links que vão ajudar a construir seu conhecimento. Principalmente os links da w3schools, que mostram todas as propriedades que eu não pude utilizar neste artigo.

A intenção é que você utilize os exemplos que detalhei aqui com todas as propriedades e métodos possíveis, assim você vai atingir um nível de conhecimento excelente, além de conseguir descobrir onde você tem dúvidas.

Com a base descrita neste artigo, mais suas tentativas com falhas e/ou acertos, você vai aprender e começar a criar suas primeiras aplicações em Javascript.

E falando em dúvidas, já sabe né? Se tiver alguma dúvida basta perguntar nos comentários abaixo. Estamos aqui para ajudar.

Outras aulas

Caso tenha perdido a aula anterior, segue o link:

Próxima aula:

Caso queira visualizar todas as aulas dessa sessão em ordem cronológica invertida: