# JavaScript para iniciantes: DOM, eventos, fetch e sua primeira lista de tarefas, com código testado

> JavaScript é o que faz a página reagir ao usuário. Veja arrays e objetos, o DOM, eventos, fetch e localStorage segundo o MDN, com três exemplos que a Redação rodou no navegador (tema escuro, projetos vindos de um JSON e uma lista de tarefas completa), um bug real que o teste revelou e vídeos em português verificados.

- Editoria: Carreira
- Autor: Redação PushTech
- Publicado em: 2026-10-24T21:38:00-03:00
- URL canônica: https://pushtech.tech/carreira/javascript-para-iniciantes-dom-eventos-fetch-e-sua-primeira-lista-de-tarefas

**JavaScript é a linguagem que faz a página reagir: clicar, digitar, filtrar, guardar e buscar dados.** É o quarto artigo da trilha "Quero virar dev em casa", e ele termina com uma lista de tarefas que funciona de verdade e guarda os dados no navegador. Pré-requisitos: [lógica de programação](/carreira/logica-de-programacao-a-habilidade-que-vem-antes-da-linguagem-com-exercicios) e uma página HTML e CSS, como a do [artigo 3](/carreira/html-e-css-como-criar-e-publicar-sua-primeira-pagina-de-verdade).

## Para quem é este artigo

Para quem já fez a primeira página e quer acrescentar interação. Tempo de estudo sugerido: de 10 a 15 horas ao longo de duas a três semanas, entre leitura, vídeos e prática (estimativa da Redação).

## O que você vai aprender

1. Variáveis, tipos, arrays e objetos em JavaScript.
2. O DOM: como o JavaScript enxerga e muda a página.
3. Eventos: reagir a cliques e formulários.
4. `fetch`: carregar dados de um arquivo ou de uma API.
5. `localStorage`: guardar dados no navegador.
6. Um mini-projeto: lista de tarefas.

## O que é JavaScript e por que é uma boa primeira linguagem

JavaScript é a linguagem que roda dentro do navegador. HTML dá a estrutura, CSS dá a aparência e **JavaScript dá o comportamento**. Por rodar em qualquer navegador, não exige instalar nada além do que você já tem, e o resultado aparece na hora. Esta é a razão pela qual a Redação recomenda começar pela web, como no [primeiro artigo da trilha](/carreira/quero-virar-dev-em-casa-por-onde-comecar-de-verdade-com-plano-de-30-dias).

## Os fundamentos que você já viu

No artigo de lógica, você usou variáveis, condições, repetições e funções, em Python e em JavaScript. A ideia é a mesma aqui. O MDN recomenda declarar variáveis com **`const` quando puder e `let` quando precisar**.

### Tipos e comparações

```javascript
console.log(typeof 42, typeof "oi", typeof true, typeof undefined);
// number string boolean undefined
console.log(3 === "3", 3 == "3");
// false true
```

Use sempre **`===`** (igualdade estrita), que não converte tipos. O `==` converte e leva a resultados surpreendentes, como `3 == "3"` ser verdadeiro. Esta é uma recomendação da Redação.

### Arrays: listas de valores

```javascript
const tarefas = ["Estudar HTML", "Estudar CSS"];
tarefas.push("Publicar no GitHub Pages");
console.log(tarefas.length, tarefas[0]);
// 3 Estudar HTML

for (const tarefa of tarefas) {
  console.log("-", tarefa);
}
```

A contagem começa em zero: `tarefas[0]` é o primeiro item.

### Objetos: dados com nome

```javascript
const pessoa = { nome: "Ana", idade: 28, estuda: true };
console.log(pessoa.nome, pessoa["idade"]);
// Ana 28
pessoa.cidade = "Recife";
```

Arrays e objetos juntos descrevem quase qualquer dado: uma lista de tarefas é um array de objetos, cada um com texto e estado. E o método `filter` cria um novo array só com o que passa em um teste:

```javascript
const pendentes = tarefas.filter((t) => !t.concluida);
```

## O DOM: como o JavaScript enxerga a página

Segundo o MDN, o DOM é uma **representação em árvore** da estrutura do HTML, criada pelo navegador, que permite acessá-la por linguagens de programação. Cada item da árvore é um **nó**. O MDN recomenda selecionar elementos com `querySelector`, que aceita seletores de CSS:

```javascript
const botao = document.querySelector("#tema");
```

Com a referência na mão, você pode mudar o texto (`textContent`), criar elementos (`createElement`), colocá-los na página (`appendChild`) e retirá-los (`remove`):

```javascript
const item = document.createElement("li");
item.textContent = "Estudar JavaScript";
lista.appendChild(item);
```

## Eventos: reagir ao que o usuário faz

Segundo o MDN, eventos são coisas que acontecem no sistema e sobre as quais ele avisa para o seu código reagir. Para reagir, você anexa um **ouvinte de evento** com `addEventListener`, o mecanismo recomendado pelo MDN. Ele recebe o tipo do evento (como `"click"`) e a função a executar:

```javascript
botao.addEventListener("click", () => {
  document.body.classList.toggle("escuro");
});
```

Em formulários, o navegador por padrão envia e recarrega a página. O MDN mostra como **`preventDefault()`** impede esse comportamento padrão, e é o que faremos na lista de tarefas.

## Exemplo 1: alternar o tema e lembrar a escolha

Acrescente um botão ao cabeçalho do currículo do artigo 3 e um arquivo `script.js`. No HTML, dentro do `<header>`, depois do `<nav>`:

```html
<button id="tema" type="button">Alternar tema</button>
```

E, antes do `</body>`:

```html
<script src="script.js"></script>
```

No CSS, acrescente o visual escuro:

```css
body.escuro { background: #0b1520; color: #e4ebf2; }
body.escuro h2 { color: #9fb3c8; }
body.escuro .cartoes li { background: #16283a; }
body.escuro main a { color: #9fb3c8; }
```

E no `script.js`:

```javascript
const botaoTema = document.querySelector("#tema");

if (localStorage.getItem("tema") === "escuro") {
  document.body.classList.add("escuro");
}

botaoTema.addEventListener("click", () => {
  const escuro = document.body.classList.toggle("escuro");
  localStorage.setItem("tema", escuro ? "escuro" : "claro");
});
```

O MDN explica que o `localStorage` guarda pares de chave e valor, **sempre como texto**, e que os dados persistem mesmo quando o navegador é fechado e reaberto. A página recomenda usar `setItem` e `getItem`. **Resultado testado:** o botão alterna o tema, e a escolha continua depois de recarregar a página. O teste também achou um problema de contraste: os links de contato ficavam azul-escuros sobre fundo escuro, e a regra `body.escuro main a` acima o corrige.

**Cuidado (da Redação):** não guarde senhas nem dados sensíveis no `localStorage`; qualquer script da página consegue lê-lo.

## `fetch`: carregar dados de um arquivo ou de uma API

Segundo o MDN, `fetch()` é uma API assíncrona que **devolve uma Promise**. Você encadeia `then()` para tratar a resposta, chama `response.json()` para obter os dados como objeto e **confere `response.ok`**, para detectar erros como o 404. Um `catch()` no fim trata falhas.

Crie um arquivo `projetos.json`:

```json
[
  { "nome": "Currículo online", "descricao": "minha primeira página, publicada no GitHub Pages" },
  { "nome": "Lista de tarefas", "descricao": "adicionar, concluir, remover e filtrar tarefas" }
]
```

Acrescente `<ul id="lista-projetos"></ul>` na seção de projetos do HTML e, no `script.js`:

```javascript
const listaProjetos = document.querySelector("#lista-projetos");

fetch("projetos.json")
  .then((resposta) => {
    if (!resposta.ok) {
      throw new Error(`Erro HTTP: ${resposta.status}`);
    }
    return resposta.json();
  })
  .then((projetos) => {
    for (const projeto of projetos) {
      const item = document.createElement("li");
      item.textContent = `${projeto.nome}: ${projeto.descricao}`;
      listaProjetos.appendChild(item);
    }
  })
  .catch((erro) => {
    listaProjetos.textContent = "Não foi possível carregar os projetos.";
    console.error(`Problema no fetch: ${erro.message}`);
  });
```

**Resultado testado:** os dois projetos aparecem na lista. **Atenção:** o `fetch` de um arquivo local costuma falhar se você abrir o HTML com duplo clique (endereço `file://`). Teste por um servidor, e o GitHub Pages já é um (a Redação rodou todos os testes por um servidor local e não testou o caso do duplo clique). Em um editor de código, procure uma extensão de servidor local, ou rode `python3 -m http.server` na pasta. A página do MDN não cobre `async/await`; ele é uma forma equivalente de escrever, e você o encontra nos vídeos.

## Mini-projeto: lista de tarefas

O usuário pode **adicionar, concluir, remover e filtrar** tarefas, e os dados ficam salvos no navegador. Crie `tarefas.html`, `tarefas.css` e `tarefas.js`.

**`tarefas.html`**

```html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Lista de tarefas</title>
  <link rel="stylesheet" href="tarefas.css" />
</head>
<body>
  <main>
    <h1>Lista de tarefas</h1>

    <form id="formulario">
      <label for="texto">Nova tarefa</label>
      <input id="texto" type="text" maxlength="80" autocomplete="off" />
      <button type="submit">Adicionar</button>
    </form>

    <div class="filtros">
      <button type="button" data-filtro="todas">Todas</button>
      <button type="button" data-filtro="pendentes">Pendentes</button>
      <button type="button" data-filtro="concluidas">Concluídas</button>
    </div>

    <ul id="lista"></ul>
    <p id="contador"></p>
  </main>
  <script src="tarefas.js"></script>
</body>
</html>
```

**`tarefas.css`**

```css
body { margin: 0; font-family: system-ui, sans-serif; background: #f5f7fa; color: #1f2933; }
main { max-width: 32rem; margin: 0 auto; padding: 1.5rem; }
form { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
form label { width: 100%; font-weight: 600; }
form input { flex: 1; padding: 0.5rem; }
button { padding: 0.5rem 0.8rem; cursor: pointer; }
.filtros { display: flex; gap: 0.5rem; margin: 1rem 0; }
.filtros button[aria-pressed="true"] { background: #102a43; color: white; }
ul { padding: 0; list-style: none; }
li { display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0; border-bottom: 1px solid #d9e2ec; }
li span { flex: 1; }
li.concluida span { text-decoration: line-through; color: #7b8794; }
```

**`tarefas.js`**

```javascript
const formulario = document.querySelector("#formulario");
const campoTexto = document.querySelector("#texto");
const lista = document.querySelector("#lista");
const contador = document.querySelector("#contador");
const botoesFiltro = document.querySelectorAll("[data-filtro]");

let tarefas = JSON.parse(localStorage.getItem("tarefas") || "[]");
let filtro = "todas";

function salvar() {
  localStorage.setItem("tarefas", JSON.stringify(tarefas));
}

function desenhar() {
  lista.textContent = "";

  const visiveis = tarefas.filter((t) => {
    if (filtro === "pendentes") return !t.concluida;
    if (filtro === "concluidas") return t.concluida;
    return true;
  });

  for (const tarefa of visiveis) {
    const item = document.createElement("li");
    if (tarefa.concluida) item.classList.add("concluida");

    const caixa = document.createElement("input");
    caixa.type = "checkbox";
    caixa.checked = tarefa.concluida;
    caixa.setAttribute("aria-label", `Concluir: ${tarefa.texto}`);
    caixa.addEventListener("change", () => {
      tarefa.concluida = caixa.checked;
      salvar();
      desenhar();
    });

    const texto = document.createElement("span");
    texto.textContent = tarefa.texto;

    const remover = document.createElement("button");
    remover.type = "button";
    remover.textContent = "Remover";
    remover.addEventListener("click", () => {
      tarefas = tarefas.filter((t) => t.id !== tarefa.id);
      salvar();
      desenhar();
    });

    item.append(caixa, texto, remover);
    lista.appendChild(item);
  }

  const pendentes = tarefas.filter((t) => !t.concluida).length;
  contador.textContent = `${pendentes} pendente(s) de ${tarefas.length}`;

  for (const botao of botoesFiltro) {
    botao.setAttribute("aria-pressed", String(botao.dataset.filtro === filtro));
  }
}

formulario.addEventListener("submit", (evento) => {
  evento.preventDefault();
  const texto = campoTexto.value.trim();
  if (texto === "") return;
  tarefas.push({ id: crypto.randomUUID(), texto, concluida: false });
  campoTexto.value = "";
  salvar();
  desenhar();
});

for (const botao of botoesFiltro) {
  botao.addEventListener("click", () => {
    filtro = botao.dataset.filtro;
    desenhar();
  });
}

desenhar();
```

### Como a Redação testou, e o bug que o teste achou

A Redação rodou a lista no Chrome, servida por um servidor local, e verificou: tarefa adicionada, texto vazio recusado, tarefa concluída (com o texto riscado), filtros de pendentes e concluídas, remoção e contador. Depois recarregou a página, e as tarefas continuaram lá.

**O teste achou um defeito na primeira versão.** O identificador de cada tarefa vinha de `Date.now()`, a hora em milissegundos. Quando três tarefas eram criadas no mesmo milissegundo, todas ficavam com o **mesmo identificador**, e remover uma **removia todas**, porque o código remove "todas as tarefas com esse identificador". Um humano raramente clica tão rápido, mas é um defeito real, e ele se mostrou assim que o teste adicionou tarefas em sequência. A versão acima usa **`crypto.randomUUID()`**, que gera um identificador único, e foi testada de novo: remover uma tarefa remove só ela.

**A lição vale para toda a sua carreira:** teste o caminho feliz e também o caso esquisito. Um bug que "nunca acontece" costuma acontecer no pior momento.

## Vídeos para estudar

Os quatro vídeos foram verificados quanto ao título, ao canal, à duração, à data e à possibilidade de incorporação. A Redação não os assistiu na íntegra nem verificou o que dizem.

_Vídeo: CURSO JAVASCRIPT (Fernanda Kipper)_

_Vídeo: Eventos DOM - Curso JavaScript #10 (Curso em Vídeo)_

_Vídeo: Aprenda Fetch API em 12 Minutos (com Exemplos Práticos) (Hashtag Dev)_

_Vídeo: Fundamentos de Programação com JavaScript Curso Completo (+8 horas de vídeo) (freeCodeCamp em Português)_

## Exercícios práticos

1. **Contador de cliques:** um botão que mostra quantas vezes foi clicado.
2. **Saudação:** um campo de nome e um botão que escreve "Olá, [nome]" na página.
3. **Tema com o tema do sistema:** descubra como ler a preferência de tema do computador e use-a como padrão.
4. **Editar tarefa:** na lista, permita editar o texto de uma tarefa.
5. **Prioridade:** acrescente uma prioridade (alta, média, baixa) a cada tarefa e ordene a lista.
6. **Erro no fetch:** troque o nome do arquivo no `fetch` por um que não existe e veja a mensagem de erro aparecer.

## Erros comuns de iniciantes

- **Selecionar o elemento antes de ele existir.** Coloque o `<script>` no fim do `<body>`, como nos exemplos.
- **Usar `==` em vez de `===`.**
- **Esquecer o `preventDefault()` no formulário**, e a página recarrega ao enviar.
- **Guardar objeto direto no `localStorage`.** Ele só guarda texto: use `JSON.stringify` para gravar e `JSON.parse` para ler.
- **Abrir o HTML com duplo clique e usar `fetch`.** Use um servidor local.
- **Identificadores repetidos.** O bug desta matéria: use um gerador de identificador único.
- **Copiar sem entender.** Escreva à mão e quebre de propósito.
- **Não olhar o console.** Aperte F12 e leia as mensagens de erro.

## Checklist de conclusão

- [ ] Sei criar arrays e objetos e percorrer um array.
- [ ] Selecionei elementos com `querySelector` e mudei o conteúdo da página.
- [ ] Usei `addEventListener` para reagir a cliques e a formulários.
- [ ] Carreguei dados com `fetch` e tratei o erro.
- [ ] Guardei e li dados no `localStorage` com `JSON`.
- [ ] Terminei a lista de tarefas e testei os casos esquisitos.
- [ ] Sei abrir o console do navegador e ler um erro.

## Próximo passo da trilha

Agora que você tem projetos no computador, o próximo artigo ensina **Git e GitHub**: guardar a história do seu código, publicar a lista de tarefas e montar um portfólio.

## Perguntas frequentes

### O que é JavaScript?
É a linguagem que roda no navegador e dá comportamento à página: reage a cliques, digitação e envio de formulários, muda o conteúdo e busca dados.

### JavaScript é uma boa primeira linguagem?
É uma boa porta de entrada pela web, porque roda no navegador que você já tem e mostra o resultado na hora. A Redação também recomenda aprender lógica antes, como no artigo anterior. É uma recomendação, e não uma regra.

### O que é o DOM?
Segundo o MDN, é uma representação em árvore da estrutura do HTML, criada pelo navegador, que permite que linguagens de programação a acessem e a modifiquem.

### O que é um evento em JavaScript?
Segundo o MDN, são coisas que acontecem no sistema e sobre as quais ele avisa, para o seu código reagir, como um clique. Você reage a elas com `addEventListener`.

### O que é fetch?
Segundo o MDN, é uma API assíncrona que devolve uma Promise e serve para buscar dados em um endereço, como um arquivo JSON ou uma API.

### O que o localStorage guarda e por quanto tempo?
Segundo o MDN, guarda pares de chave e valor como texto, e os dados persistem mesmo com o navegador fechado e reaberto. Para objetos e arrays, use `JSON.stringify`.

### Posso guardar senhas no localStorage?
Não. Qualquer script da página consegue ler o `localStorage`. Esta é uma recomendação da Redação.

### Por que o fetch não funciona quando abro o HTML com duplo clique?
Em geral, porque o endereço começa com `file://` e o navegador bloqueia a leitura de arquivos por `fetch`. Use um servidor local ou publique no GitHub Pages. Esta é uma observação da Redação, e o caso do endereço `file://` não foi testado por ela.

## Fontes

- [DOM scripting introduction](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/DOM_scripting), MDN Web Docs (Mozilla)
- [Introduction to events](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Events), MDN Web Docs (Mozilla)
- [Network requests (fetch)](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Network_requests), MDN Web Docs (Mozilla)
- [Using the Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API), MDN Web Docs (Mozilla)
- [Storing the information you need: Variables](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Variables), MDN Web Docs (Mozilla)
