9.3 Manipular tabelas HTML
As tabelas são uma importante forma de exibir dados nas páginas HTML. As tags básicas para criar uma tabela são:
- table para definir a tabela,
- tr para criar uma nova linha
- td para criar uma nova coluna em uma linha.
- tag th que define uma célula de cabeçalho na tabela.
Para definir uma nova tabela via programação JavaScript, é possível utilizar os mesmos comandos das seções anteriores e indicar que um texto deve ser filho de td, td filho de tr e tr filho de table. Contudo, para facilitar a manipulação das tabelas em um programa, existem os métodos insertRow(num) e insertCell(nun), que inserem, respectivamente, uma linha e uma coluna na tabela. Então, pode-se criar uma tabela na página ou uma referência a uma tabela existente, e manipular sua estrutura adicionando novas linhas e colunas. Ambos os métodos recebem como parâmetro (num) a posição da linha ou coluna a ser inserida na tabela. 0 (zero) identifica a linha ou coluna inicial. Para inserir uma linha no final da tabela, deve-se passar como argumento o valor -1.
Também é possível remover uma linha da tabela a partir do método deleteRow(num). O parâmetro indica o número da linha a ser removida. Já a propriedade length, sempre relacionada ao tamanho de um objeto, pode igualmente ser utilizada para recuperar o número de linhas da tabela a partir da instrução table.rows.length.
Outro importante recurso para a manipulação de tabelas via programação JavaScript é ter acesso ao conteúdo de uma célula da tabela, o que pode ser feito com a criação de referências aos índices que apontam para uma linha e coluna da tabela. Utilizar table.rows[0].cells[0] recupera o conteúdo da primeira célula da tabela. Ou seja, colocar essa instrução dentro de um laço de repetição vai permitir a obtenção de todo o conteúdo da tabela.
Vamos construir um exemplo que explora esses métodos e propriedades na manipulação de tabelas via JavaScript. O terceiro programa deste capítulo deve cadastrar filmes na localStorage e exibir uma tabela contendo título, gênero e uma coluna que permita ao usuário selecionar o filme para exclusão. A Figura 9.7 exibe a página desse programa com alguns filmes cadastrados.
Crie um novo arquivo HTML e salve-o com o nome de ex9_3.html. O código a ser inserido nesse arquivo é descrito no Exemplo 93.
Exemplo 9.3 – Página HTML que vai exibir uma tabela de filmes (ex9_3.html)
«1DOCTYPE htnl> chtpíl lang="pt-br"> <head> «neta charset=”UTF-8"> «neta nane="viewport" content="width=device-width, initial-scale=1.0"> «neta http-equiv="X-UA-Conpatible" content="ie=edge"> «link rel="stylesheet" href="css/bootstrap.nin.css"> «link rel="stylesheet" href="css/esttlos.css"> <titLe>Exenpl.o 9.3</title> </head> <body> <div class="container-fluid"> «div class="row"> «div class="col-sn-4"> <tng src="ing/ex9_3.jpg" alt="Fi.lnes" cl.ass="ingProg"> </div> «div class="coi-sn-8"> <hl>Meus Filnes Favoritos«/hl> <p> «labei for="inTitulo">Título do Filme:«/label> <input type="text" class="form-control" id="inTitulo"> </p> <p> <label for="inGenero">Gênero:«/label> «input type="text" class="form-control" id="inGenero"> </p> <p class-'aDireita"> «input type="button" class="btn btn-primary" value="Adicionar" id="btAdicionar"> </p> <table class="table table-striped" id="tbFilmes"> <tr> <th>Título do Filme</th> <th>Gênero</th> <th> <label><input type="checkbox" id="ckTodos"> Todos </label> </th> </tr> </table> «p class="aDireita"> «input type="button" class="btn btn-danger" value="Excluir Selecionados" id="btExcluir"> </P> </div> </div> </div> «script src="js/ex9_3.js">«/script> </body> </html>
Observe que a tabela é inicialmente criada no código HTML, e sua linha de cabeçalho, inserida. As demais linhas com os filmes informados pelo usuário serão acrescentadas pelo programa JavaScript. Vamos a ele… crie um novo arquivo e salve-o na pasta js com o nome ex9_3.js. Como o programa é grande, vamos apresentar suas funções em partes. Começamos pela function adicionarFilme().
Programa JavaScript que insere linhas e colunas em uma tabela (js/ex9_3.js)
function adicionarFilme() { // cria referência aos campos de entrada var inTitulo = document.getElementById("inTitulo"); var inGenero = docunent.getElementById("inGenero"); var titulo = inTitulo.value; // obtém conteúdo dos campos var genero = inGenero.value; // valida preenchimento if (titulo == "" || genero == "") { alert("Informe corretamente os dados"); inTitulo.focus(); return; } // cria referência ao elemento tbFilmes var tbFilmes = document.getElementById("tbFilmes"); // chama function que irá inserir filme na tabela inserirLinha(tbFilmes, titulo, genero); // chama function que irá gravar dados em localStorage gravarFilme(titulo, genero); inTitulo.value = // limpa campos de entrada inGenero.value = ""; inTitulo.focus(); // posiciona o cursor em inTitulo } var btAdicionar = document.getElementById("btAdicionar"); btAdicionar.addEventListener("click", adicionarFilme);
Essa função cria referência aos campos de entrada, valida o seu preenchimento e delega as tarefas de inserir uma nova linha na tabela e um novo filme na localStorage às funções inserirLinha() e gravarFilme(). A function inserirLinha() contém as principais novidades dessa seção. Acrescente esse código no arquivo ex9_3. js.
function inserirLinha(tabela, titulo, genero) { var linha = tabela.insertRow(-l); // adiciona uma linha na tabela var coll = linha.insertCell(0); // cria colunas na linha inserida var col2 = linha.insertCell(l); var col3 = linha.insertCell(2); coll.textContent = titulo; // joga um conteúdo em cada célula col2.textContent = genero; col3.innerHTML = "<input type='checkbox'>"; // innerHTML renderiza código }
Observe que a function recebe os parâmetros tabela, titulo e genero, sendo tabela uma referência à tbFilnes que é a table existente no código HTML. Nela, acrescentamos uma linha com o método insertRow(), em que -1 indica que a linha será inserida no final da tabela. O próximo passo é inserir as colunas nessa linha. Para isso, recorremos ao método insertCell(). E, por último, é feita a atribuição do conteúdo de cada célula.
Para que o campo tipo checkbox seja renderizado na terceira coluna da tabela, utilizamos a propriedade innerHTML discutida na seção 2.5. Essa é uma forma segura de utilizar essa propriedade, visto que o conteúdo a ser renderizado não vem de um campo preenchido pelo usuário, e sim de um dado fixo inserido pelo programa.
Caso você tenha ficado com alguma dúvida quanto à diferença entre as propriedades textContent e innerHTML, faça o seguinte teste: substitua a innerHTML por textContent e rode o programa. Você vai observar que com textContent o navegador exibe o texto <input type.. .>, e não uma caixa de seleção para o usuário selecionar.
Vamos então à segunda function chamada pelo adicionarFilme(). Ela recebe os parâmetros titulo e genero e os grava em localStorage. Para separar os filmes, é utilizado o delimitador
function gravarFilme(titulo, genero) { // se há filmes salvos em localStorare ... if (localStorage.getItem("filmesTitulo")) { // ... obtém os dados e acrescenta e o título/gênero informado var filmesTitulo = localStorage.getItem("filmesTitulo") + + titulo; var filmesGenero = localStorage.getItem("filmesGenero") + + genero; localStorage.setltem( "filmesTitulo", filmesTitulo); // grava dados localStorage.setltem("filmesGenero", filmesGenero); // em localStorage } else { // senão, é a primeira inclusão (salva sem delimitador) localStorage. setltem("filmesTitulo", titulo); localStorage. setltem("filmesGenero", genero); } }
A partir das funções apresentadas, o programa lê os dados informados pelo usuário, exibe-os nas tabelas a partir dos métodos insertRow() e insertCell() e os salva em localStorage. A função destacada a seguir visa recuperar os dados dos filmes salvos em localStorage e os exibir quando a página for carregada. Assim, o usuário não perde a sua lista de filmes favoritos salvos na execução anterior do programa.
function recuperarFilmes() { //se houver dados salvos em localStorage if (localStorage.getItem("filmesTitulo")) { // obtém conteúdo e converte em elementos de vetor (na ocorrência ";") var titulos = localStorage.getItem("filmesTitulo").split(";"); var generos = localStorage.getltem("filmesGenero").split(";"); // cria referência ao elemento tbFilmes var tbFilmes = document.getElementById("tbFilmes"); // percorre elementos do vetor e os insere na tabela for (var i = 0; i < titulos.length; i++) { inserirLinha(tbFilmes, titulosfi], generos[i]); } } } recuperarFilmesO;
A function recuperarFilmesO verifica se há dados salvos em localStorage e, se houver, joga o conteúdo de cada campo em elementos de vetor. Em seguida, cria referência à tabela tbFilmes para que na repetição possa também chamar a function inserirLinha() que fará a exibição dos dados na página. Você pode testar o funcionamento do programa com as funções criadas até este ponto. Elas são responsáveis pelo processo de inclusão e exibição dos dados em uma tabela da página.
As funções discutidas a seguir permitem remover os filmes da tabela e também da localStorage. Começamos pela discussão da function anônima associada ao evento change do checkbox ckTodos. Ao clicar sobre ele, todos os filmes são marcados ou desmarcados, conforme ilustra a Figura 9.8.
Acrescente as seguintes linhas ao programa ex9_3.js. Então, faça o teste e observe que, quando clicado, o status de ckTodos é atribuído a todos os filmes da tabela.
JJJ
// cria referência ao checkbox ckTodos (na linha de título da tabela) var ckTodos = document.getElementById("ckTodos"); // executa função anônima quando houver una troca de status ckTodos.addEventListener("change", function () { // cria referência à tabela e aos canpos input (filhos da tabela) var tbFilmes = docunent.getElenentById("tbFilnes"); var ckExcluir = tbFilmes.getElementsByTagName("input"); var status = ckTodos.checked; // obtém status de ckTodos ... // ... e percorre os demais checkbox para aplicar este status for (var i = 1; i < ckExcluir.length; i++) { ckExcluir[i].checked = status; } });
Naturalmente, esse código poderia ficar em uma função nomeada como as demais desse programa. Vamos retomando alguns recursos discutidos ao longo do livro para relembrá-los. Sobre a function, observe que é criada uma referência à tag table tbFilmes e, em seguida, todos os campos input filhos de tbFilmes são recuperados com o método getElementsByTagName(). Na sequência do programa, obtém-se o status de ckTodos (marcado ou desmarcado) para aplicá-lo a todos os inputs da tabela, a partir de uma estrutura de repetição. Perceba que no for a variável i inicia com o valor 1, pois o input de índice 0 é o próprio ckTodos.
A function removerFilme() é um pouco mais complexa, pois envolve as ações de remover o filme da localStorage e também retirar uma linha da tabela. Acrescente essa última function no arquivo ex9_3. js para concluir o nosso programa de cadastro de filmes.
function removerFilmesO { // cria referência à tabela e aos campos input (filhos da tabela) var tbFilmes = document.getElementById("tbFilmes"); var ckExcluir = tbFilmes.getElementsByTagName("input"); var temSelecionado = false; // para verificar se há filmes selecionados // percorre campos input type checkbox da tabela (exceto "Todos" no título) for (var i = 1; i < ckExcluir.length; i++) { if (ckExcluir[i].checked) { // se está selecionado temSelecionado = true; // muda valor da "flag" break; // sai da repetição } } //se não temSelecionado (se valor da variável é false) if (!temSelecionado) { alert("Não há filmes selecionados para exclusão"); return; } // solicita confirmação de exclusão dos filmes selecionados if (confirm("Confirma Exclusão dos Filmes Selecionados?")) { // exclui conteúdo armazenado em localStorage localStorage. removeltem( "filmesTitulo"); localStorage. removeltem( "filmesGenero"); // primeiro irá gravar em localStorage os filmes não selecionados for (i = 1; i < ckExcluir.length; i++) { // se não está selecionado (para exclusão) íf (!ckExcluír[í].checked) { // obtém o conteúdo da tabela (coluna 0: título; coluna 1: gênero) var titulo = tbFílnes.rows[í].cells[0].textContent; var genero = tbFílnes.rows[í].cells[l].textContent; gravarFílne(título, genero); // chama gravarFílne com dados da tabela } } // agora irá remover as linhas selecionadas (do fim para o início) for (i = ckExcluir.length - 1; i > 0; i--) { if (ckExcluir[i].checked) { tbFilmes.deleteRow(i); // remove a linha } } ckExcluir[0].checked = false; // desmarca ckTodos (que é o input 0) } } var btExcluir = document.getElementById("btExcluir"); btExcluir.addEventListener("clíck", removerFilmes);
As duas primeiras linhas de código da function remover Filmes () são idênticas às da função anônima discutida anteriormente, pois aqui também é necessário recuperar os campos input filhos de tbFilmes. Em seguida, temos um conjunto de comandos utilizados para verificar se há algum filme selecionado para exclusão, a fim de alertar o usuário e evitar a execução desnecessária de comandos.
O processo de exclusão ocorre se o usuário confirmar a execução dessa tarefa no if contendo o método confirm(). Para isso, primeiro se removem as variáveis armazenadas em localStorage (o usuário poderia ter selecionado todos os filmes para exclusão). Na sequência, obtém-se o conteúdo das células da tabela das linhas não marcadas e chama-se a function que grava esses dados em localStorage. Remover tudo primeiro para depois incluir novamente parece um pouco estranho… Mas, para essa estrutura de armazenamento, é o modo mais simples de executar essa tarefa.
O terceiro e último for dessa function visa retirar os filmes exibidos na tabela. Um detalhe importante nessa etapa é que a repetição deve ocorrer do final para o início, pois os índices dos elementos são alterados dinamicamente quando se remove um deles. Assim, se excluirmos primeiro o filme de índice 0, todos os demais sobem uma posição e a exclusão não funcionaria corretamente se mais de um filme estivesse selecionado.
Como destacado no capítulo anterior, os dados armazenados na Local Storage podem ser excluídos se o usuário limpar o histórico de navegação em seu browser. Portanto, cuidado com as informações salvas nesse local pelos programas desenvolvidos em nossos exemplos. Eles objetivam simular o processo de persistência de dados e devem ser substituídos por aplicações que enviam dados para Web Services a fim de evitar possíveis perdas de dados.
Estamos nos aproximando do final do livro. O próximo capítulo contém mais alguns exemplos com programas maiores como esse último de cadastro de filmes. Selecionei três exercícios de fixação dos assuntos abordados neste capítulo para você praticar o que foi visto aqui. Um ótimo trabalho!! Ah… não se esqueça de que um exemplo de resposta com o programa de cada enunciado está disponível no site da editora.