Como construir um aplicativo TODO com Vanilla JavaScript (e armazenamento local)

1. Comece com os ativos necessários

Para tornar o layout um pouco mais exclusivo, usaremos algumas ilustrações SVG feitas à mão e uma fonte personalizada tirada de ga-analytics#sendElementsClickEvent”>Envato Elements.

ga-analytics#sendElementsClickEvent”>VERÃO - PACOTE DE FONTESVERÃO - PACOTE DE FONTESVERÃO - PACOTE DE FONTES
ga-analytics#sendElementsClickEvent”>VERÃO – PACOTE DE FONTES no Envato Elements

Vale a pena notar que a maioria desses ativos vem de um tutorial anterior. Na verdade, também usaremos muitas das técnicas de posicionamento que aprendemos neste tutorial, então vale a pena lê-lo.

Como construir um formulário SVG responsivo feito à mãoComo construir um formulário SVG responsivo feito à mãoComo construir um formulário SVG responsivo feito à mão

2. Continuar com a marcação da página

Começaremos com um SVG e um div recipiente:

Sprites SVG

Como fizemos muitas vezes no passado, como uma boa prática, armazenaremos todos os SVGs como symbols em um contêiner sprite SVG. Então, vamos renderizá-los na tela sempre que precisarmos chamando o use elemento.

Aqui está a marcação para o sprite SVG:

Observe o preserveAspectRatio="none" atributo que anexamos à maioria das ilustrações. Fizemos isso porque, como veremos mais adiante, nossos ícones serão dimensionados e perderão suas dimensões iniciais.

Recipiente

O contêiner incluirá um formulário, um div elemento e uma lista ordenada vazia:

Dentro do formulário, teremos uma entrada e um botão de envio junto com seus SVGs associados:

Observe o name atributo que adicionamos ao campo de entrada. Posteriormente usaremos este atributo para acessar o valor de entrada após o envio do formulário.

Observação: Em nossa demonstração, o autofocus atributo do campo de texto não funcionará. Na verdade, ele lançará o seguinte erro que você pode ver se abrir o console do navegador:

O erro de origem cruzada devido ao atributo de foco automáticoO erro de origem cruzada devido ao atributo de foco automáticoO erro de origem cruzada devido ao atributo de foco automático

No entanto, se você executar este aplicativo localmente (não como um projeto Codepen), esse problema não existirá. Como alternativa, você pode definir o foco via JavaScript.

Dentro de divcolocaremos três aninhados divs e o SVG associado. Nesta seção, acompanharemos o número total de tarefas (remanescentes e concluídas):

Finalmente, os itens da lista ordenada serão adicionados dinamicamente através do JavaScript.

3. Definir alguns estilos básicos

Com a marcação pronta, vamos continuar com alguns estilos de redefinição:

4. Defina os estilos principais

Vamos agora discutir os principais estilos de nosso aplicativo TODO.

Estilos de contêiner

O contêiner terá uma largura máxima com conteúdo centralizado horizontalmente:

Estilos de formulário

Em telas pequenas, todos os elementos do formulário serão empilhados:

O layout do formulário em telas pequenasO layout do formulário em telas pequenasO layout do formulário em telas pequenas

No entanto, em viewports de 600 pixels de largura e acima, o layout do formulário será alterado da seguinte forma:

O layout do formulário em telas médias e superioresO layout do formulário em telas médias e superioresO layout do formulário em telas médias e superiores

Atentemos para duas coisas:

  • Em viewports amplos, a entrada terá o dobro do tamanho do botão.
  • Os SVGs serão elementos absolutamente posicionados e ficarão abaixo de seu controle de formulário adjacente. Novamente, para uma explicação mais detalhada, dê uma olhada neste tutorial anterior.

Aqui estão os estilos para esta seção:

Estilos de estatísticas

Em seguida, vamos olhar para a barra de status que nos dará um relatório rápido sobre o número total de tarefas.

Em telas pequenas terá a seguinte aparência empilhada:

O layout das estatísticas em telas pequenasO layout das estatísticas em telas pequenasO layout das estatísticas em telas pequenas

No entanto, em viewports de 600 pixels de largura e acima, deve mudar da seguinte forma:

O layout de estatísticas em telas médias e acimaO layout de estatísticas em telas médias e acimaO layout de estatísticas em telas médias e acima

Atentemos para duas coisas:

  • Em viewports amplas, todas as crianças div elementos terão larguras iguais.
  • Da mesma forma que os SVGs anteriores, este também será absolutamente posicionado e atuará como uma imagem de fundo que cobre toda a seção.

Os estilos relacionados:

Estilos de tarefas

O layout das tarefas, que geraremos dinamicamente na próxima seção, ficará assim:

A disposição das tarefasA disposição das tarefasA disposição das tarefas

Cada tarefa que será representada por um li terá duas partes.

A representação de marcação de uma tarefaA representação de marcação de uma tarefaA representação de marcação de uma tarefa

Na primeira parte, você verá uma caixa de seleção junto com o nome da tarefa. Na segunda parte, você notará um botão de exclusão para remover a tarefa.

Aqui estão os estilos relacionados:

Quando uma tarefa é incompleto, uma caixa de seleção vazia aparecerá. Por outro lado, se uma tarefa for marcada como concluído, uma marca de seleção aparecerá. Além disso, seu nome receberá 50% de opacidade, bem como uma linha através dele.

Aqui estão os estilos responsáveis ​​por esse comportamento:

Finalmente, abaixo estão os estilos para o botão de exclusão:

5. Adicione o JavaScript

Neste ponto, estamos prontos para construir a funcionalidade principal de nosso aplicativo de lista TODO. Vamos fazer isso!

No envio do formulário

Cada vez que um usuário envia o formulário pressionando o botão Digitar chave ou o Enviar botão, faremos o seguinte:

  1. Interrompa o envio do formulário, evitando assim o recarregamento da página.
  2. Pegue o valor que está contido no campo de entrada.
  3. Supondo que o campo de entrada não esteja vazio, criaremos um novo objeto literal que representará a tarefa. Cada tarefa terá um ID exclusivo, um nome e estará ativa (não concluída) por padrão.
  4. Adicione esta tarefa ao tasks variedade.
  5. Armazene a matriz no armazenamento local. O armazenamento local suporta apenas strings, então para fazer isso, temos que usar o JSON.stringify() método para converter os objetos dentro da matriz em strings.
  6. Ligar para createTask() função para representar visualmente a tarefa na tela.
  7. Limpe o formulário.
  8. Dê foco ao campo de entrada.

Aqui está o código relevante:

Criar uma tarefa

o createTask() A função será responsável por criar a marcação da tarefa.

Por exemplo, aqui está a estrutura para a tarefa “Dar um passeio”:

A estrutura de marcação para uma tarefaA estrutura de marcação para uma tarefaA estrutura de marcação para uma tarefa

Duas coisas são importantes aqui:

  • Se a tarefa for concluída, a marca de seleção aparecerá.
  • Se a tarefa não for concluída, é span elemento receberá o contenteditable atributo. Este atributo nos dará a capacidade de editar/atualizar seu nome.

Segue abaixo a sintaxe desta função:

Atualizar uma tarefa

Uma tarefa pode ser atualizada de duas maneiras diferentes:

  • Ao alterar seu status de “incompleto” para “concluído” e vice-versa.
  • Modificando seu nome caso a tarefa esteja incompleta. Lembre-se que, neste caso, o span elemento tem o contenteditable atributo.

Para acompanhar essas mudanças, aproveitaremos a input evento. Este é um evento aceitável para nós porque se aplica tanto a input elementos e elementos com contenteditable ativado.

O complicado é que não podemos anexar este evento diretamente aos elementos de destino (caixa de seleção, span) porque são criados dinamicamente e não fazem parte do DOM no carregamento da página.

Graças à delegação do evento, anexaremos o input evento para a lista pai que faz parte da marcação inicial. Em seguida, através do target propriedade desse evento, vamos verificar os elementos nos quais o evento ocorreu e chamar o updateTask() função:

Dentro de updateTask() função, faremos o seguinte:

  1. Pegue a tarefa que precisa ser atualizada.
  2. Verifique o elemento que acionou o evento. Se o elemento tiver o contenteditable atributo (ou seja, é o span elemento), vamos definir o nome da tarefa igual ao spanconteúdo de texto de.
  3. Caso contrário (ou seja, é a caixa de seleção), alternaremos o status da tarefa e sua checked atributo. Além disso, também alternaremos o contenteditable atributo do adjacente span.
  4. Atualize o valor do tasks chave no armazenamento local.
  5. Ligar para countTasks() função.

Aqui está a sintaxe para esta função:

Remover uma tarefa

Podemos remover uma tarefa através do botão “fechar”.

Botão para remover uma tarefaBotão para remover uma tarefaBotão para remover uma tarefa

Semelhante à operação de atualização, não podemos anexar diretamente um evento a este botão porque ele não está no DOM quando a página é carregada.

Mais uma vez obrigado à delegação do evento, anexaremos um click event à lista pai e execute as seguintes ações:

  1. Verifique se o elemento clicado é o botão “fechar” ou seu filho SVG.
  2. Se isso acontecer, vamos pegar o id do item da lista pai.
  3. Passe isso id para o removeTask() função.

Aqui está o código relevante:

Dentro de removeTask() função, faremos o seguinte:

  1. Remover do tasks array a tarefa associada.
  2. Atualize o valor do tasks chave no armazenamento local.
  3. Remova o item de lista associado.
  4. Ligar para countTasks() função.

Aqui está a sintaxe para esta função:

Tarefas de Contagem

Como já discutimos, muitas das funções acima incluem o countTask() função.

Sua função é monitorar as tarefas quanto a alterações (adições, atualizações, exclusões) e atualizar o conteúdo dos elementos relacionados.

Contar tarefasContar tarefasContar tarefas

Aqui está sua assinatura:

Impedir a adição de novas linhas

Cada vez que um usuário atualiza o nome de uma tarefa, ele não deve ser capaz de criar novas linhas pressionando o botão Digitar chave.

Impedir várias linhasImpedir várias linhasImpedir várias linhas

Para desabilitar esta funcionalidade, mais uma vez vamos aproveitar a delegação do evento e anexar o keydown evento à lista, assim:

Note que neste cenário apenas o span elementos podem acionar esse evento, então não há necessidade de fazer uma verificação adicional como esta:

Dados persistentes no carregamento da página

Até agora, se fecharmos o navegador e navegarmos para o projeto de demonstração, nossas tarefas desaparecerão.

Mas, espere, isso não é 100% verdade! Lembre-se que cada vez que fazemos uma manipulação de tarefa, também armazenamos o tasks matriz no armazenamento local. Por exemplo, no Chrome, para ver as chaves e valores de armazenamento local, clique no botão Inscrição guia em seguida, expanda o Armazenamento Local menu e, finalmente, clique em um domínio para visualizar seus pares chave-valor.

No meu caso, aqui estão os valores para o tasks chave:

Um exemplo com armazenamento localUm exemplo com armazenamento localUm exemplo com armazenamento local

Portanto, para exibir essas tarefas, primeiro precisamos recuperá-las do armazenamento local. Para fazer isso, usaremos o JSON.parse() método que converterá as strings de volta em objetos JavaScript.

Em seguida, armazenaremos todas as tarefas no familiar tasks variedade. Lembre-se de que, se não houver dados no armazenamento local (por exemplo, na primeira vez que visitamos o aplicativo), essa matriz estará vazia. Em seguida, temos que percorrer o array e, para cada tarefa, chamar o método createTask() função. E isso é tudo!

O trecho de código correspondente:

Conclusão

Ufa! Obrigado por acompanhar nesta longa jornada pessoal. Espero que você tenha adquirido algum conhecimento novo hoje, que poderá aplicar em seus próprios projetos.

Vamos nos lembrar do que construímos:

Sem dúvida, construir tal aplicativo com uma estrutura JavaScript pode ser mais estável, fácil e eficiente (repintar o DOM é caro). No entanto, saber resolver esse tipo de exercício com JavaScript simples ajudará você a obter uma compreensão sólida de seus fundamentos e a torná-lo um desenvolvedor de JavaScript melhor.

Antes de encerrar, deixe-me propor duas ideias para estender este exercício:

  • Use a API HTML Drag and Drop ou uma biblioteca JavaScript como Sortable.js para reordenar as tarefas.
  • Armazene dados (tarefas) na nuvem em vez do navegador. Por exemplo, substitua o armazenamento local por um banco de dados em tempo real como o Firebase.

Como sempre, muito obrigado pela leitura!

Mais aplicativos Vanilla JavaScript

Se você quiser aprender a criar pequenos aplicativos com JavaScript simples, confira os seguintes tutoriais:

Deixe uma resposta