WDD 131: Fundamentos da Web Dinâmica

S05 – Atividade de Aprendizagem: localStorage JavaScript

Visão Geral

Há momentos em que o armazenamento de dados não confidenciais proporcionaria uma melhor experiência ao usuário e ofereceria algumas melhorias no desempenho do site. Os mecanismos da API de Armazenamento na Web (Web Storage) fornecem a capacidade de armazenar informações em um formato de chave-valor com base em um agente de usuário ou origem específicos. Nesta atividade, você descobrirá a finalidade e o escopo do mecanismo localStorage disponível por meio da interface Storage. Você usará esse conhecimento para direcionar e exibir alguns dados de tráfego de clientes individuais em uma página.

Os dados de armazenamento na web são salvos em um arquivo SQLite em uma subpasta ou arquivo na pasta de perfil do usuário.

Preparação

Aqui estão alguns dados de comparação entre cookies, localStorage (armazenamento local) e sessionStorage (armazenamento de sessão).

Tabela 1: Tabela de Comparação de Métodos de Armazenamento Local
Cookies localStorage sessionStorage
Capacidade ~4 KB ~5 MB ~5 MB
Expira Definido manualmente Nunca Ao fechar aba/janelas
Acessível a partir de Qualquer janela Qualquer janela Mesma aba

Cada um desses métodos armazena dados no cliente do agente do usuário (navegador) real, e não no servidor.

Vantagens do localStorage

Você pode armazenar dados que não sejam string no localStorage. Uma abordagem comum é usar o método JSON.stringify() para converter os dados em uma string antes de armazená-los e, então, usar o método JSON.parse() para convertê-los de volta ao seu formato original quando você recuperá-los do localStorage. Isso permite que você armazene e recupere estruturas de dados mais complexas, como objetos e arrays, no localStorage.

Demonstração do localStorage

Quais são algumas das diferenças entre armazenamento de sessão e local?

Uma grande diferença é que os dados do localStorage são persistentes (permanecem) mesmo quando a sessão do navegador expira. Já as variáveis de sessão não.

Usando a interface Storage, quais são alguns métodos incorporados a esse objeto de interface e o que eles fazem?

De toda a lista de métodos [key(), getItem(), setItem(), removeItem(), clear()], os mais comuns são getItem() e setItem() que você usará nas tarefas. Interface de Armazenamento da API de Armazenamento na Web

Visão Geral da Atividade

Esta atividade retoma o exercício dos capítulos favoritos do Livro de Mórmon. Os capítulos inseridos pelo usuário não persistem entre as vezes que acessa a aplicação. Em outras palavras, eles não são armazenados em lugar nenhum. Nesta atividade, você usará o localStorage para armazenar a lista de capítulos do Livro de Mórmon para o usuário.

As instruções gerais para esse problema seriam "Aprimorar a aplicação de capítulos favoritos do Livro de Mórmon para que as entradas dos usuários persistam entre os acessos à aplicação usando a API localStorage". Aceite esse desafio pensando primeiro em uma solução para esse problema e depois mapeando-a no papel.

O plano direcionado é criar um array de entradas de livros e capítulos válidas feitas pelo usuário. Então esse array pode ser armazenado no localStorage como uma grande string JSON usando JSON.stringify(). Isso significa que você precisa carregar esse array ao carregar a aplicação com os dados processados do localStorage, SE o componente localStorage nomeado existir. Após o carregamento, você precisará preencher a lista com os valores armazenados.

Então, em vez de ter duas funções que basicamente fazem a mesma coisa, você criará uma única função que anexa a lista de capítulos favoritos com o botão de exclusão correspondente, uma vez no carregamento e também quando uma nova entrada é feita.

Instruções da Atividade

Observe que o código fornecido é apenas um exemplo. Você pode ter usado nomes de variáveis diferentes, etc. Você nunca deve simplesmente copiar o código.
  1. Faça uma cópia da sua aplicação do Livro de Mórmon, copiando o HTML, CSS e JavaScript da atividade de aprendizagem anterior para uma pasta chamada semana05.
  2. Abra o arquivo JavaScript. Declare um array chamado arrayCapitulos e atribua-o aos resultados de uma função definida chamada obterListaDeCapitulos (Esta função não existe ainda).
  3. Na mesma declaração e atribuição de variável de array, adicione uma condição OR composta para atribuir a ela um array vazio, caso este seja o primeiro acesso do usuário ou se o item localStorage estiver ausente.

    Isso funciona porque a função pode não retornar nada, então é falsa, ou seja, ela voltará a atribuir o array vazio a arrayCapitulos.

    Exemplo
            const input = document.querySelector('#capfav');
    const botao = document.querySelector('#botao');
    const lista = document.querySelector('#lista');
    
    let arrayCapitulos = obterListaDeCapitulos() || [];
    Explicações do Código

    As três primeiras linhas estabelecem referências aos elementos DOM que você usará no programa. Observe que elas fazem referência apenas aos objetos do elemento HTML, não a nenhuma propriedade.

    A declaração de array inicializa a variável arrayCapitulos com a lista de capítulos retornados pela função obterListaDeCapitulos() ou um array vazio se a chamada de função retornar null (nulo) ou undefined (indefinido).

  4. Agora preencha a lista de capítulos exibida. Use forEach no arrayCapitulos para processar cada entrada chamada capitulo. Use uma função de seta dentro do loop para chamar uma nova função definida chamada exibirLista e passar a ela o argumento do capitulo. Dessa forma, cada entrada será processada (ou seja, anexada à lista).
    Exemplo
                  arrayCapitulos.forEach(capitulo=> {
      exibirLista(capitulo);
    });
  5. Altere o ouvinte de evento de clique de botão para executar apenas as seguintes tarefas (as outras tarefas na função original serão usadas em uma função separada chamada exibirLista):
    1. Verifique se a entrada está vazia; se não estiver, então...
    2. Chame exibirLista com o argumento input.value
    3. Faça push no input.value para o arrayCapitulos
    4. Atualize o localStorage com o novo array chamando uma função denominada definirListaDeCapitulos
    5. Defina o input.value como nada
    6. Retorne o foco para a entrada.
    Exemplo com // comentários de código
            button.addEventListener('click', () => {
      if (input.value != '') {  // certifique-se de que a entrada não esteja vazia
        exibirLista(input.value); // chama a função que gera o capítulo enviado
        arrayCapitulos.push(input.value);  // adicione o capítulo ao array
        definirListaDeCapitulos(); // atualize o localStorage com o novo array
        input.value = ''; // limpe a entrada
        input.focus(); // defina o foco de volta para a entrada
      }
    });
  6. Crie a função definida exibirLista que recebe um parâmetro chamado item.
  7. Coloque todo o código que cria um item de lista do ouvinte de evento de clique de botão anterior nesta nova função exibirLista e use o parâmetro item como entrada. Uma função excluirCapitulo precisará ser chamada dentro do evento de clique do botão "delete" para remover o capítulo do array e do localStorage.
    Exemplo: exibirLista()
            function exibirLista(item) {
      let li = document.createElement('li');
      let botaoExcluir = document.createElement('button');
      li.textContent = item; // observe o uso do 'item' do parâmetro exibirLista
      botaoExcluir.textContent = '❌';
      botaoExcluir.classList.add('delete'); // isso faz referência à regra CSS .delete{width:fit-content;} para dimensionar o botão de exclusão
      li.append(botaoExcluir);
      list.append(li);
      botaoExcluir.addEventListener('click', function () {
        list.removeChild(li);
        excluirCapitulo(li.textContent); // observe esta nova função que é necessária para remover o capítulo do array e do localStorage.
        input.focus(); // defina o foco de volta para a entrada
      });
      console.log('Eu gosto de copiar código em vez de digitá-lo e tentar entendê-lo.');
    }
  8. Defina a função definirListaDeCapitulos para definir o item de localStorage que você já nomeou. Use JSON.stringify() para transformar o array em string.
    Exemplo: definirListaDeCapitulos()
                  function definirListaDeCapitulos() {
      localStorage.setItem('minhaListaFavoritosLDM', JSON.stringify(arrayCapitulos));
    }
  9. Defina a função obterListaDeCapitulos para obter o item de localStorage. Nenhum parâmetro é necessário. Como essa função retorna para um array em espera, você precisará usar JSON.parse na string.
    Exemplo: obterListaDeCapitulos()
    function obterListaDeCapitulos() {
      return JSON.parse(localStorage.getItem('minhaListaFavoritosLDM'));
    }
  10. Por fim, defina a função excluirCapitulo com um parâmetro chamado capitulo que faz três coisas.
    1. Reformate o parâmetro capitulo para se livrar ❌ do que é processado no final da string do capítulo quando você chama a função excluirCapitulo. Use o método string.slice() para extrair o último caractere.
      capitulo = capitulo.slice(0, capitulo.length – 1); // isso corta o último caractere
    2. Redefina o array arrayCapitulos usando o método array.filter para retornar tudo, exceto o capítulo a ser removido.
      arrayCapitulos = arrayCapitulos.filter((item) => item !== capitulo);
    3. Chame a função definirListaDeCapitulos para atualizar o item de localStorage.
    Exemplo: excluirCapitulo()
    function excluirCapitulo(capitulo) {
      capitulo = capitulo.slice(0, capitulo.length – 1);
      arrayCapitulos = arrayCapitulos.filter(item => item !== capitulo);
      definirListaDeCapitulos();
    }

Teste

Teste a aplicação adicionando e excluindo capítulos. Faça uma atualização completa, esvazie o cache e veja se os capítulos persistem. Você também pode apagar todo o conteúdo do localStorage da aplicação na aba Applications (Aplicações) no DevTools.

Recursos Opcionais

  1. Vídeo: WebStorage, localStorage e SessionStorage em Javascript – YouTube
  2. Vídeo: Aprenda a Criar e Manipular Cookies com JavaScript – YouTube
  3. Uma abordagem dinâmica e estruturada para essa atividade seria usar o Set objeto em JavaScript. Set nos permite adicionar e excluir itens do objeto Set conforme necessário.
  4. Referência: API de Armazenamento na Web – MDN
  5. A API IndexedDB é um método mais robusto de armazenamento de dados no lado do cliente e é mais parecida com um banco de dados.
  6. A API Cache é um método de armazenamento de dados usado especificamente para armazenar dados em cache para uso offline.