<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD - Gerenciador de Produtos</title>
DOCTYPE html
: Define que é HTML5lang="pt-BR"
: Especifica idioma português brasileirocharset="UTF-8"
: Suporte a caracteres especiais e acentosviewport
: Torna o site responsivo em dispositivos móveis<div class="stats">
<div class="stat-card">
<div class="stat-number" id="totalProdutos">0</div>
<div class="stat-label">Total de Produtos</div>
</div>
<div class="stat-card">
<div class="stat-number" id="valorTotal">R$ 0,00</div>
<div class="stat-label">Valor Total em Estoque</div>
</div>
</div>
Explicação:
class="stats"
: Container principal das estatísticasid="totalProdutos"
e id="valorTotal"
: IDs únicos para o
JavaScript acessar e atualizar os valores<form id="produtoForm">
<div class="form-row">
<div class="form-group">
<label for="nome">Nome do Produto</label>
<input type="text" id="nome" name="nome" required>
</div>
<!-- ... outros campos ... -->
</div>
</form>
Elementos importantes:
id="produtoForm"
: JavaScript usa este ID para capturar o envioname="nome"
: Nome do campo usado pelo FormDatarequired
: Validação HTML5 nativafor="nome"
: Liga o label ao input correspondentetype="number" step="0.01"
: Para preços com centavos<div class="search-container">
<input type="text" class="search-input" id="searchInput" placeholder="🔍 Buscar...">
<select id="filterCategoria">
<option value="">Todas as categorias</option>
<option value="Eletrônicos">Eletrônicos</option>
<!-- ... outras opções ... -->
</select>
</div>
id="searchInput"
: JavaScript monitora mudanças neste campoid="filterCategoria"
: Filtro adicional por categoria<table id="produtosTable">
<thead>
<tr>
<th>#</th>
<th>Nome</th>
<!-- ... outras colunas ... -->
</tr>
</thead>
<tbody id="produtosBody">
<!-- Conteúdo gerado dinamicamente pelo JavaScript -->
</tbody>
</table>
id="produtosBody"
: JavaScript insere as linhas aquithead
: Cabeçalho fixo da tabelatbody
: Corpo da tabela onde os dados são inseridoslet produtos = [];
let editandoId = null;
Explicação:
produtos = []
: Array que armazena todos os produtos na memóriaeditandoId = null
: Controla se estamos editando (null = modo criar, ID = modo editar)const form = document.getElementById('produtoForm');
const tbody = document.getElementById('produtosBody');
const submitBtn = document.getElementById('submitBtn');
// ... outros elementos
Por que fazer isso:
form.addEventListener('submit', handleSubmit);
cancelBtn.addEventListener('click', cancelarEdicao);
searchInput.addEventListener('input', filtrarProdutos);
filterCategoria.addEventListener('change', filtrarProdutos);
Eventos monitorados:
submit
: Quando o formulário é enviadoclick
: Quando botão cancelar é clicado input
: Toda vez que usuário digita na buscachange
: Quando categoria do filtro mudafunction handleSubmit(e) {
e.preventDefault(); // Impede recarregamento da página
const formData = new FormData(form); // Captura dados do formulário
const produto = {
id: editandoId || gerarId(), // Se editando usa ID existente, senão gera novo
nome: formData.get('nome').trim(), // Remove espaços extras
preco: parseFloat(formData.get('preco')), // Converte string para número
categoria: formData.get('categoria'),
descricao: formData.get('descricao').trim(),
dataCriacao: editandoId ?
produtos.find(p => p.id === editandoId).dataCriacao :
Date.now() // Mantém data original se editando
};
if (editandoId) {
// MODO UPDATE
const index = produtos.findIndex(p => p.id === editandoId);
produtos[index] = produto; // Substitui produto existente
editandoId = null; // Sai do modo edição
submitBtn.textContent = 'Adicionar Produto';
cancelBtn.style.display = 'none';
} else {
// MODO CREATE
produtos.push(produto); // Adiciona novo produto
}
form.reset(); // Limpa formulário
renderizarTabela(); // Atualiza interface
atualizarEstatisticas();
}
Detalhes importantes:
e.preventDefault()
: Impede comportamento padrão do formulário (recarregar página)FormData
: API moderna para capturar dados de formuláriotrim()
: Remove espaços em branco no início/fimparseFloat()
: Converte string para número decimalfindIndex()
: Encontra posição do produto no arrayfunction renderizarTabela(produtosFiltrados = produtos) {
if (produtosFiltrados.length === 0) {
tbody.innerHTML = `<tr><td colspan="7" class="empty-state">Mensagem</td></tr>`;
return;
}
tbody.innerHTML = produtosFiltrados.map((produto, index) => `
<tr>
<td>${index + 1}</td>
<td><strong>${produto.nome}</strong></td>
<td><strong>${formatarPreco(produto.preco)}</strong></td>
<td><span style="...">${produto.categoria}</span></td>
<td>${produto.descricao || 'Sem descrição'}</td>
<td>${formatarData(produto.dataCriacao)}</td>
<td class="actions">
<button class="btn-edit" onclick="editarProduto(${produto.id})">✏️ Editar</button>
<button class="btn-delete" onclick="excluirProduto(${produto.id})">🗑️ Excluir</button>
</td>
</tr>
`).join('');
}
Conceitos-chave:
produtosFiltrados = produtos
- se não passar parâmetro, usa
array completoproduto.descricao || 'Sem descrição'
- se vazio, usa
texto padrãofunction editarProduto(id) {
const produto = produtos.find(p => p.id === id); // Encontra produto pelo ID
if (!produto) return; // Se não encontrar, sai da função
// Preenche formulário com dados existentes
document.getElementById('nome').value = produto.nome;
document.getElementById('preco').value = produto.preco;
document.getElementById('categoria').value = produto.categoria;
document.getElementById('descricao').value = produto.descricao;
// Muda para modo edição
editandoId = id;
submitBtn.textContent = 'Atualizar Produto';
cancelBtn.style.display = 'inline-block';
// Scroll suave para o formulário
document.querySelector('.form-container').scrollIntoView({
behavior: 'smooth'
});
}
Técnicas utilizadas:
if (!produto) return
- sai cedo se não encontrarfunction excluirProduto(id) {
if (confirm('Tem certeza que deseja excluir este produto?')) {
produtos = produtos.filter(p => p.id !== id); // Remove produto do array
renderizarTabela(); // Atualiza interface
atualizarEstatisticas();
// Se estava editando o produto excluído, cancela edição
if (editandoId === id) {
cancelarEdicao();
}
}
}
Pontos importantes:
confirm()
: Diálogo nativo de confirmação do browserfunction filtrarProdutos() {
const termo = searchInput.value.toLowerCase(); // Busca case-insensitive
const categoria = filterCategoria.value;
const produtosFiltrados = produtos.filter(produto => {
const nomeMatch = produto.nome.toLowerCase().includes(termo);
const categoriaMatch = produto.categoria.toLowerCase().includes(termo);
const categoriaFilter = !categoria || produto.categoria === categoria;
return (nomeMatch || categoriaMatch) && categoriaFilter;
});
renderizarTabela(produtosFiltrados);
}
Lógica de filtro:
toLowerCase()
: Busca sem distinção de maiúsculas/minúsculasincludes()
: Verifica se string contém o termo(A || B) && C
- (nome OU categoria) E
filtro-categoria// Gerar ID único baseado em timestamp + random
function gerarId() {
return Date.now() + Math.random();
}
// Formatar data em português
function formatarData(timestamp) {
return new Date(timestamp).toLocaleDateString('pt-BR');
}
// Formatar preço em Real brasileiro
function formatarPreco(preco) {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL'
}).format(preco);
}
APIs modernas utilizadas:
Date.now()
: Timestamp atual em milissegundostoLocaleDateString()
: Formatação de data localizadaIntl.NumberFormat
: API de formatação internacional de números/moedasfunction atualizarEstatisticas() {
const total = produtos.length; // Conta elementos do array
const valorTotal = produtos.reduce((sum, produto) => sum + produto.preco, 0);
document.getElementById('totalProdutos').textContent = total;
document.getElementById('valorTotal').textContent = formatarPreco(valorTotal);
}
Array.reduce() explicado:
sum
- valor que vai sendo somadoproduto
- elemento atual do array0
- começa somando do zerosum + produto.preco
- soma preço ao acumuladorhandleSubmit
FormData
captura dadosprodutos
renderizarTabela()
atualiza interfaceatualizarEstatisticas()
recalcula totaiseditarProduto(id)
editandoId = id
)handleSubmit
detecta modo ediçãoexcluirProduto(id)
confirm()
pede confirmaçãofilter()
remove do arrayfiltrarProdutos
renderizarTabela(produtosFiltrados)
mostra apenas resultadosEste CRUD demonstra conceitos fundamentais do JavaScript moderno: manipulação de DOM, arrays, objetos, eventos, APIs nativas e programação funcional!