KingHost
Canais iMasters

Javascript

Tudo sobre Javascript não-obstrutivo - Parte 03 (Criando e destruindo conteúdo)

A força principal do DOM não é apenas a leitura, mas também a modificação e criação da estrutura do documento em "mãos". Para isso, nós temos vários métodos ao nosso dispor.

Criando novo conteúdo

createElement(element)
Cria um novo elemento
createTextNode(string)
Cria um novo nó do tipo texto com o conteúdo 'string'.

Elementos recém criados não são adicionados ao documento imediatamente. Eles ficam numa espécie de 'limbo', até que eles sejam adicionados dentro de algum nó. Essas funções devem ser aplicadas ao documento ao invez de um nó.

Javascript:  
	
	mynewparagraph=document.createElement('p');
	mynewtext=document.createTextNode('this is a new paragraph');  

Alterando o conteúdo já existente

setAttribute(attribute,value)
Adiciona ou altera o atributo 'attribute' com o valor 'value'.
appendChild(child)
Adiciona um nó como childNode do objeto. O nó precisa ser um objeto, você não pode usar uma string.
cloneNode()
Copia todo o nó e seus childNodes.
hasChildNodes()
Verifica se um objeto tem childNodes, retornando true se for o caso.
insertBefore(newchild,oldchild)
Adicionar newschild antes do oldchild na árvore do documento.
removeChild(oldchild)
Remove o oldchild .
replaceChild(newchild,oldchild)
Substitui o oldchild pelo newchild.
removeAttribute(attribute)
Remove o atributo 'attribute' do objeto.

O exemplo da figura

Vamos dizer que temos links para algumas imagens, e elas devem abrir em uma nova janela sem usar Javascript, ou abaixo quando o Javascript está disponivel.

HTML:

<ul id="imglist">
   <li>
      <a href="home.gif" target="_blank">Home (nova janela)</a>
   </li>
   <li>
      <a href="home_on.gif" target="_blank">Home ativo (nova janela)</a>
   </li>
   <li>
      <a href="jscsshtml.gif" target="_blank">
         HTML-CSS-Javascript (nova janela)
      </a>
   </li>
</ul>  

Agora, quando o Javascript e o DOM estão disponíveis, nós queremos que:

. Esconda a palavra "(nova janela)" do link.
. Adicione um evento manipulador para chamar a função popw()

Essa função deve:

. Mostrar a imagem abaixo do link quando ela já não estiver lá visível.
. Remover a imagem, se ela já estiver lá (para evitar que o link adicione a imagem mais uma vez).
. Fazer a imagem desaparecer quando clicada.

Esse problema não é difícil de resolver:

Javascript:

function imgpop()
{
   var il,imga,imgatxt;    // get all LIs in imagelist, loop over them
   il=document.getElementById('imglist').getElementsByTagName('li');
   for(i=0;i<il.length;i++)
   {  
      // pega o primeiro link no LI
      imga=il[i].getElementsByTagName('a')[0];
      // deleta o texto "(nova janela)" no texto do link
      // (que é o nodeValue do primeiro nó)
      imgatxt=imga.firstChild;
      imgatxt.nodeValue=imgatxt.nodeValue.replace(/ \(nova janela\)/,'');
      // adiciona o evento manipulador para chamar popw()
      imga.onclick=function(){return popw(this);}
      // imga.onkeypress=function(){return popw(this);}
   }
}  

Agora, a função na popw() nós precisaremos de usar alguns dos métodos ensinados acima:

Javascript:
	
function popw(o)
{
   var newimg;  //s e já houver alguma imagem no parentNode (li)
   if(o.parentNode.getElementsByTagName('img').length>0)   
   {  // deleta-a
o.parentNode.removeChild(o.parentNode.getElementsByTagName('img')[0]);
   } 
   else
   {  // senão, cria uma nova imagem e adiciona um manipulador que   
      // a remove quando clicada    
      newimg=document.createElement('img');
      newimg.style.display='block';
      newimg.onclick=function(){this.parentNode.removeChild(this);};
      newimg.src=o.href;    
      o.parentNode.appendChild(newimg)
   }   
    return false;
}

Veja este exemplo imagepop em ação.

O exemplo do selecionador de datas

Vamos supor, por exemplo, que nós temos um formulário que tem campos de data e nós queremos oferecer aos usuários com Javascript ativado um selecionador de data. Outros usuários simplesmente digitarão as datas manualmente. Não vamos discutir a função de selecionar data agora, mas vamos focar em como chamá-la.

Começaremos com o HTML necessário. Para ver quais campos devem ter um selecionador de data, nós vamos adicionar a propriedade "class" neles.

HTML:  

<h1>Plano de voo</h1>
<form action="nosend.php" method="post" onsubmit="return check(this);">
   <p>Passo 1 de 4</p>
   <h2>Favor selecionar as datas</h2>
   <p>  
      <label for="startdate">Data de inicio</label>
       <input type="text" class="data" id="startdate" name="startdate" />
   </p>
   <p>
      <label for="enddate34;>Data de término</label>
      <input type="text" class="data" id="enddate" name="enddate" />
   </p>
   <p>
      <input type="submit" value="Enviar" />
   </p>
</form>  

Nós iremos varrer todos as tags input do documento e verificar quais tem o className que contêm "date" (lembre-se, elementos podem ter mais de uma classe no atributo class).

Se for o caso, nós criaremos um novo link e um texto para ele. Nós adicionaremos o texto do link como um child do link e adicionaremos um evento manipulador para chamar nossa rotina de selecionar data

Uma vez criado o link, nós adicionaremos ele depois do input.

Javascript:  

function addPickerLink()
{
   var inputs,pickLink,pickText;
   // varre todos os inputs
   inputs=document.getElementsByTagName('input');
   for(i=0;i<inputs.length;i++)
   {
      // se o atributo class contem 'data'
      if(/data/.test(inputs[i].className))
      {    // cria um novo link e um texto
         pickLink=document.createElement('a');
         pickText=document.createTextNode('selecionar uma data');
// adiciona o texto como child do link
         pickLink.appendChild(pickText);
         // muda o href do link para # e chama a função
         // picker() quando clicado
         pickLink.setAttribute('href','#');
         pickLink.onclick=function(){picker(this);return false;};
         // pickLink.onkeypress=function(){picker(this);return false;};
         // adiciona um novo link ao nó pai do campo input
         inputs[i].parentNode.appendChild(pickLink)
      }
   }
}

Veja este exemplo em ação.  

Agora todos os campos com a class data tem um link seguido dele que chama a função picker();

Tudo que precisamos agora é dizer a função picker() onde aplicar o valor retornado.

Como nós mandamos o próprio objeto do link como parâmetro para a função picker(), precisamos acessar o elemento que vem antes do link, o input.

Javascript:

function picker(o)
{
   alert('Isso é apenas uma simulação.');
   o.previousSibling.value='26/04/1975';
}  

Bom, mas não perfeito. Como nós adicionamos um link como o último child no nó pai do input, poderia acontecer facilmente do previusSibling (lembre-se do artigo anterior, previusSibling retorna o elemento anterior no mesmo nível na árvore do documento) ser um espaço em branco e não o input! Consequentemente, nós temos de varrer através do previousSibling até encontrar um elemento.

Javascript:

function picker(o)
{
   alert('Isso é apenas uma simulação.') // no real function today
   while(o.previousSibling.nodeType!=1)
   {
      o=o.previousSibling;
   }
   o.previousSibling.value='26/04/1975';
}  

Varrer através de loop sempre deixa mais lento. Para evitar que prejudiquemos o desempenho, nós precisaríamos mudar a nossa função.

Melhorando a função addPickerLink()

É sempre fácil usar o appendChild(), mas isto nos torna dependente da marcação. O que acontece, por exemplo, se nós precisarmos adicionar uma tag span com um asterisco próximo ao input para indicar que ele é obrigatório?

O macete é usar insertBefore no nextSibling do nosso input.

Javascript:

function addPickerLink()
{
   var inputs,pickLink,pickText;    // varre todos os inputs
   inputs=document.getElementsByTagName('input');
   for(i=0;i<inputs.length;i++)
   {
      // se tem 'data' no atributo class
      if(/data/.test(inputs[i].className))
      {
         // cria um novo link e o seu texto
         pickLink=document.createElement('a');
         pickText=document.createTextNode('selecionar uma data');
         // adicionar o texto como um child do link
         pickLink.appendChild(pickText);
         // muda o href para # e chama a 
         //funcao picker quando clicada
         pickLink.setAttribute('href','#');
         pickLink.onclick=function(){picker(this)};
         // pickLink.onkeypress=function(){picker(this)};
         // adiciona o novo link diretamente apos o input
         inputs[i].parentNode.appendChild(pickLink)
         inputs[i].parentNode.insertBefore(pickLink,inputs[i].nextSibling);
      }
   }  
}  

Veja a nova função addPickerLink() em funcionamento.  

Lembretes

Isso é tudo. Com essas ferramentas nós somos capazes de acessar e alterar qualquer elemento de um documento, e podemos melhorar a experiência do usuário sem tornar o site dependente do Javascript.

Pode ser um pouco confuso a princípio, mas uma vez que você entendeu um pouco como o DOM funciona, vai se tornando mais fácil a cada vez que você o usa.

Alguns problemas comuns são:

. Certifique-se de verificar os elementos antes de tentar acessá-los. Alguns navegadores são bons em verificar object.nextSibling.nodeName e retornar falso quando não há nextSibling ou se é um textNode, outros navegadores entretanto dão erro se você tentar acessar um atributo de um elemento que não exista.

. Não confie muito na marcação, quebras de linha podem ser lidas como um novo nó, ou a marcação pode mudar, e você não quer ter que mudar seu script toda vez que a marcação é alterada.

. Ler o conteúdo de um elemento é ler os valores de seus childNodes, não o valor do elemento em si. document.getElementsByTagName('h2')[0].nodeValue é vazio, e document.getElementsByTagName('h2')[0].firstChild.nodeValue não é.

. Quando verificar por nodeNames e atributos, certifique-se de manter caso sensitivo (não diferenciar maiúsculas e minúsculas). Alguns navegadores transformam os elementos de maiúsculas e outros em minúsculas.

. Evite usar muitos loops, se você tem a chance de criar a marcação que você precisa trabalhar, vá e adicione IDs nos elementos onde forem necessários.

E sobre o innerHTML?

Quando o Internet Explorer 4 chegou, o innerHTML nasceu, um jeito fácil de gerar e modificar conteúdo. Era um modo para ler conteúdo de um elemento muito mais fácil do que as recomendação original do W3C. Esse é um caso especial, um elemento que contém childNodes que são elementos e nós queremos ler todo o conteudo.

Para fazer isso somente com DOM, você precisa fazer o trabalho chato de verificar o nodeType e ler os valores de cada childNode. innerHTML é muito mais fácil de usar, mas tem alguns inconvenientes. Por exemplo, você não tem nenhuma referência aos elementos que você criou com ele. Além disso, o HTML só é relacionado com o HTML, não com o XML, e DOM foi feito para atravessar qualquer marcação.


Comente também

6 Comentários

Vinícius Bruno Costa Pinto
Vinícius Bruno Costa Pinto

mto boa os seus exemplos principalmente p/ mim já q ñ sou um dos bons programadores e apanho feito um trouxa desse programa p/ programar me ajudou mto cara vlew...

Thalis Dias Valle
Thalis Dias Valle

Parabéns pelo artigo. Muito útil, senvindo inclisive como referência de estudo.

Abraços
www.thalisvalle.com

Rodolpho Porto
Rodolpho Porto

Muito boa a sua forma de explicar, mandou bem no artigo, eu só tive necessidade de usar isso com xml mas nunca assim como vc mostrou.

Diego Fleury
Diego Fleury

traduziu bem o tutorial, parabéns

acertei na minha "previsão" quando comentei no seu ultimo post:
http://www.imasters.com.br/artigo/4337/javascript/tudo_sobre_javascript_nao-obstrutivo_-_parte_02_alcancando_os_objetos/

André Metzen
André Metzen

Sim Diego, como eu havia comunicado no primeiro artigo da série, essas matérias são traduzidas e adaptadas do artigo original de Christian Heilmann, com a devida autorização do autor. Quem preferir a versão original em inglês: http://onlinetools.org/articles/unobtrusivejavascript/
Abraços

Gustavo Soares
Gustavo Soares

Olá Andre, primeiramente parabens pela materia.
Estou com problemas:
Eu crio elementos input e coloco atributos como type text, até ai tudo bem, ele cria e mostra certinho na pagina quando eu chamo a funcao.
O problema é que nao consigo submeter esses campos no formulario...
se eu exibo o codigo fonte da pagina nao mostra os elementos, vc sabe porque?

Qual a sua opinião?

Comentários considerados ofensivos serão moderados.

Parceiros

IBM
PagSeguro
Internet Innovation
Dialhost
HostNet
Tecla
KingHost
DotStore
Dinamize