Aplicações completas e trechos de código para você estudar, copiar e adaptar. Todos integrados com o JSON Server.
Edite o código abaixo e clique em ▶ Executar para ver o resultado em tempo real. Este é o mesmo princípio que ferramentas como CodePen e JSFiddle usam!
<!-- Lista de cards com Tailwind -->
<div class="container">
<h1>Lista de Tarefas</h1>
<form id="form-tarefa">
<input type="text" id="input-tarefa"
placeholder="Nova tarefa..." />
<button type="submit">Adicionar</button>
</form>
<ul id="lista"></ul>
<p id="contagem">0 tarefas</p>
</div>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; padding: 20px; background: #f8fafc; }
.container {
max-width: 420px;
margin: 0 auto;
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}
h1 { font-size: 1.4rem; margin-bottom: 16px; color: #1e293b; }
form {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
input[type="text"] {
flex: 1;
padding: 8px 12px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 0.9rem;
outline: none;
}
input:focus { border-color: #6366f1; }
button {
background: #6366f1;
color: white;
border: none;
padding: 8px 14px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
button:hover { background: #4f46e5; }
#lista { list-style: none; margin-bottom: 12px; }
#lista li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 12px;
margin-bottom: 6px;
border-radius: 8px;
background: #f8fafc;
border: 1px solid #e2e8f0;
cursor: pointer;
transition: all 0.2s;
}
#lista li:hover { border-color: #6366f1; }
#lista li.feito {
text-decoration: line-through;
color: #94a3b8;
background: #f1f5f9;
}
.btn-del {
background: #fee2e2;
color: #dc2626;
border: none;
padding: 3px 8px;
border-radius: 5px;
font-size: 0.75rem;
cursor: pointer;
}
#contagem { font-size: 0.8rem; color: #94a3b8; text-align: right; }
const form = document.getElementById('form-tarefa');
const input = document.getElementById('input-tarefa');
const lista = document.getElementById('lista');
const contagem = document.getElementById('contagem');
let tarefas = [];
form.addEventListener('submit', (e) => {
e.preventDefault();
const texto = input.value.trim();
if (!texto) return;
tarefas.push({ id: Date.now(), texto, feito: false });
input.value = '';
renderizar();
});
function renderizar() {
lista.innerHTML = '';
tarefas.forEach(t => {
const li = document.createElement('li');
if (t.feito) li.classList.add('feito');
const span = document.createElement('span');
span.textContent = t.texto;
span.addEventListener('click', () => {
t.feito = !t.feito;
renderizar();
});
const btn = document.createElement('button');
btn.textContent = 'Excluir';
btn.className = 'btn-del';
btn.addEventListener('click', () => {
tarefas = tarefas.filter(x => x.id !== t.id);
renderizar();
});
li.appendChild(span);
li.appendChild(btn);
lista.appendChild(li);
});
const feitas = tarefas.filter(t => t.feito).length;
contagem.textContent = `${feitas} / ${tarefas.length} concluídas`;
}
<div class="calc">
<div id="display">0</div>
<div class="teclado">
<button class="btn-cinza" onclick="limpar()">C</button>
<button class="btn-cinza" onclick="apagar()">⌫</button>
<button class="btn-cinza" onclick="inserir('%')">%</button>
<button class="btn-op" onclick="inserir('/')">÷</button>
<button onclick="inserir('7')">7</button>
<button onclick="inserir('8')">8</button>
<button onclick="inserir('9')">9</button>
<button class="btn-op" onclick="inserir('*')">×</button>
<button onclick="inserir('4')">4</button>
<button onclick="inserir('5')">5</button>
<button onclick="inserir('6')">6</button>
<button class="btn-op" onclick="inserir('-')">−</button>
<button onclick="inserir('1')">1</button>
<button onclick="inserir('2')">2</button>
<button onclick="inserir('3')">3</button>
<button class="btn-op" onclick="inserir('+')">+</button>
<button class="btn-zero" onclick="inserir('0')">0</button>
<button onclick="inserir('.')">.</button>
<button class="btn-igual" onclick="calcular()">=</button>
</div>
</div>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #1c1c2e; display: flex; justify-content: center; padding: 24px; font-family: sans-serif; }
.calc { width: 260px; background: #16213e; border-radius: 20px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
#display {
padding: 16px 20px 8px;
text-align: right;
font-size: 2.2rem;
font-weight: 300;
color: white;
min-height: 70px;
word-break: break-all;
}
.teclado { display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; background: #0f3460; }
button {
padding: 18px;
font-size: 1rem;
background: #16213e;
color: white;
border: none;
cursor: pointer;
transition: background 0.15s;
font-family: inherit;
}
button:hover { background: #0f3460; }
button:active { background: #e94560; }
.btn-cinza { background: #1a1a2e; color: #a0a0a0; }
.btn-op { color: #e94560; font-weight: bold; }
.btn-zero { grid-column: span 2; }
.btn-igual { background: #e94560; font-weight: bold; }
.btn-igual:hover { background: #c73652; }
let expr = '';
let novo = true; // true = próximo dígito reinicia a expressão
function inserir(tecla) {
if (novo) { expr = ''; novo = false; }
expr += tecla;
document.getElementById('display').textContent = expr;
}
function limpar() {
expr = '';
novo = false;
document.getElementById('display').textContent = '0';
}
function apagar() {
expr = expr.slice(0, -1);
document.getElementById('display').textContent = expr || '0';
}
function calcular() {
try {
// Function() é mais seguro que eval() para expressões simples
const resultado = Function('"use strict"; return (' + expr + ')')();
document.getElementById('display').textContent =
Number.isFinite(resultado) ? +resultado.toFixed(10) : 'Erro';
expr = String(resultado);
novo = true; // próximo dígito começa nova expressão
} catch {
document.getElementById('display').textContent = 'Erro';
expr = '';
}
}
<div class="crono">
<h2>⏱ Cronômetro</h2>
<div id="display">00:00</div>
<div class="controles">
<button id="btn-play" onclick="iniciar()">▶ Iniciar</button>
<button id="btn-pause" onclick="pausar()" disabled>⏸ Pausar</button>
<button onclick="zerar()">↺ Zerar</button>
</div>
<div id="historico"></div>
</div>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; background: #0f172a; min-height: 100px; display: flex; align-items: center; justify-content: center; padding: 20px; }
.crono {
text-align: center;
background: #1e293b;
border-radius: 20px;
padding: 28px 40px;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
min-width: 260px;
}
h2 { color: #94a3b8; font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 16px; }
#display {
font-family: 'Courier New', monospace;
font-size: 4rem;
font-weight: bold;
color: #38bdf8;
margin-bottom: 24px;
letter-spacing: 0.05em;
}
.controles { display: flex; gap: 8px; justify-content: center; margin-bottom: 16px; }
button {
padding: 9px 18px;
border: none;
border-radius: 8px;
font-size: 0.82rem;
cursor: pointer;
background: #334155;
color: #e2e8f0;
transition: background 0.2s;
}
button:hover:not(:disabled) { background: #475569; }
button:disabled { opacity: 0.35; cursor: default; }
#btn-play { background: #0ea5e9; color: white; }
#btn-play:hover:not(:disabled) { background: #0284c7; }
#historico { font-size: 0.8rem; color: #475569; }
.volta { padding: 4px 0; border-top: 1px solid #334155; display: flex; justify-content: space-between; }
let segundos = 0;
let intervalo = null;
// setInterval executa uma função repetidamente a cada N milissegundos
function iniciar() {
if (intervalo) return; // evita criar dois intervalos
document.getElementById('btn-play').disabled = true;
document.getElementById('btn-pause').disabled = false;
intervalo = setInterval(() => {
segundos++;
atualizar();
}, 1000); // dispara a cada 1000ms = 1 segundo
}
// clearInterval cancela o intervalo criado por setInterval
function pausar() {
clearInterval(intervalo);
intervalo = null;
document.getElementById('btn-play').disabled = false;
document.getElementById('btn-pause').disabled = true;
// Registra volta no histórico
const div = document.createElement('div');
div.className = 'volta';
const num = document.querySelectorAll('.volta').length + 1;
div.innerHTML = `<span>Volta ${num}</span><span>${formatar(segundos)}</span>`;
document.getElementById('historico').prepend(div);
}
function zerar() {
clearInterval(intervalo);
intervalo = null;
segundos = 0;
atualizar();
document.getElementById('historico').innerHTML = '';
document.getElementById('btn-play').disabled = false;
document.getElementById('btn-pause').disabled = true;
}
function atualizar() {
document.getElementById('display').textContent = formatar(segundos);
}
// padStart(2, '0') garante 2 dígitos: 5 → "05"
function formatar(s) {
const min = String(Math.floor(s / 60)).padStart(2, '0');
const seg = String(s % 60).padStart(2, '0');
return `${min}:${seg}`;
}
<div class="conversor">
<h2>🌡️ Conversor de Temperatura</h2>
<div class="campo">
<label>Celsius (°C)</label>
<input type="number" id="celsius"
oninput="converter('celsius')" placeholder="0">
</div>
<div class="campo">
<label>Fahrenheit (°F)</label>
<input type="number" id="fahrenheit"
oninput="converter('fahrenheit')" placeholder="32">
</div>
<div class="campo">
<label>Kelvin (K)</label>
<input type="number" id="kelvin"
oninput="converter('kelvin')" placeholder="273.15">
</div>
<div id="info"></div>
</div>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; background: linear-gradient(135deg, #1e1b4b, #312e81); min-height: 100px; display: flex; align-items: center; justify-content: center; padding: 20px; }
.conversor {
background: white;
border-radius: 16px;
padding: 28px;
width: 100%;
max-width: 340px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h2 { font-size: 1rem; color: #1e1b4b; margin-bottom: 20px; text-align: center; }
.campo { margin-bottom: 14px; }
label {
display: block;
font-size: 0.75rem;
color: #6b7280;
font-weight: 700;
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
input {
width: 100%;
padding: 10px 14px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 1.1rem;
font-weight: 600;
color: #111827;
outline: none;
transition: border 0.2s;
}
input:focus { border-color: #6366f1; }
#info {
margin-top: 12px;
font-size: 0.82rem;
color: #6366f1;
text-align: center;
min-height: 20px;
font-weight: 600;
}
// oninput dispara a cada caractere digitado — ideal para conversão em tempo real
function converter(origem) {
const val = parseFloat(document.getElementById(origem).value);
if (isNaN(val)) {
// Limpa os outros campos se o input estiver vazio
['celsius', 'fahrenheit', 'kelvin']
.filter(id => id !== origem)
.forEach(id => document.getElementById(id).value = '');
document.getElementById('info').textContent = '';
return;
}
// 1. Converte tudo para Celsius primeiro (base comum)
let celsius;
if (origem === 'celsius') celsius = val;
else if (origem === 'fahrenheit') celsius = (val - 32) * 5 / 9;
else celsius = val - 273.15; // kelvin
// 2. Calcula as outras escalas a partir de Celsius
const fahrenheit = celsius * 9 / 5 + 32;
const kelvin = celsius + 273.15;
// 3. Preenche os campos (exceto o que o usuário está digitando)
if (origem !== 'celsius') document.getElementById('celsius').value = celsius.toFixed(2);
if (origem !== 'fahrenheit') document.getElementById('fahrenheit').value = fahrenheit.toFixed(2);
if (origem !== 'kelvin') document.getElementById('kelvin').value = kelvin.toFixed(2);
// 4. Curiosidade contextual
let info = '';
if (celsius <= 0) info = '🧊 Ponto de congelamento da água';
else if (celsius >= 100) info = '♨️ Ponto de ebulição da água';
else if (celsius >= 36 && celsius <= 37.5) info = '🧑 Temperatura corporal normal';
else if (celsius >= 20 && celsius <= 26) info = '😊 Temperatura ambiente agradável';
document.getElementById('info').textContent = info;
}
Estas aplicações estão disponíveis no servidor local. Certifique-se de que o JSON Server está rodando
(npm start).
CRUD completo de contatos com inserção, edição, exclusão e listagem via JSON Server. Usa Fetch API com async/await e manipulação dinâmica do DOM.
Filtragem de contatos por cidade e categoria usando os parâmetros de query do JSON Server. Demonstra busca com filtros combinados.
Login e registro de usuários com JSON Server. Sessão mantida no sessionStorage. Permite cadastro de novos usuários e logout.
Exibição de locais no mapa usando a API Mapbox. Os dados dos locais vêm do JSON Server (coleção locais). Requer conta Mapbox.
Crie conta gratuita com o e-mail XXXXXX@sga.pucminas.br em mapbox.com para obter a API Key.
Dashboard de gráficos usando Chart.js com dados vindos do JSON Server (coleção lancamentos). Gráficos de barras e pizza.
Técnica de navegar entre páginas passando dados via URL (query string). Lista de cidades carregada do JSON Server com página de detalhes.
Padrões de código que você usará com frequência na disciplina.
function renderizarLista(itens, containerId) {
const container = document.getElementById(containerId);
container.innerHTML = ''; // Limpa antes de renderizar
if (itens.length === 0) {
container.innerHTML =
'<p class="vazio">Nenhum item encontrado.</p>';
return;
}
itens.forEach(item => {
const el = document.createElement('div');
el.className = 'card';
el.dataset.id = item.id; // Guarda o ID no elemento
el.innerHTML = `
<h3>${item.nome}</h3>
<p>${item.descricao}</p>
`;
container.appendChild(el);
});
}
document
.getElementById('meu-form')
.addEventListener('submit', async (e) => {
e.preventDefault(); // Evita reload da página
// Lê os valores dos campos
const nome = document.getElementById('nome').value.trim();
const email = document.getElementById('email').value.trim();
// Validação básica
if (!nome || !email) {
alert('Preencha todos os campos!');
return;
}
const novoItem = { nome, email };
try {
const resposta = await fetch('/api/itens', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(novoItem)
});
if (resposta.ok) {
e.target.reset(); // Limpa o formulário
await carregarItens(); // Atualiza a lista
}
} catch (erro) {
alert('Erro ao salvar: ' + erro.message);
}
});
// Guarda o ID do item sendo editado
let idEditando = null;
async function abrirEdicao(id) {
idEditando = id;
// Busca os dados atuais do item
const resposta = await fetch(`/api/contatos/${id}`);
const contato = await resposta.json();
// Preenche o formulário do modal
document.getElementById('edit-nome').value = contato.nome;
document.getElementById('edit-email').value = contato.email;
// Exibe o modal
document.getElementById('modal').style.display = 'flex';
}
async function salvarEdicao() {
const dadosAtualizados = {
nome: document.getElementById('edit-nome').value,
email: document.getElementById('edit-email').value
};
await fetch(`/api/contatos/${idEditando}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dadosAtualizados)
});
document.getElementById('modal').style.display = 'none';
carregarContatos(); // Atualiza a lista
}
function mostrarLoading(containerId) {
document.getElementById(containerId).innerHTML = `
<div class="loading">
<div class="spinner"></div>
<p>Carregando...</p>
</div>
`;
}
function mostrarErro(containerId, msg) {
document.getElementById(containerId).innerHTML = `
<div class="erro">
<p>❌ ${msg}</p>
<button onclick="carregarDados()">Tentar novamente</button>
</div>
`;
}
async function carregarDados() {
mostrarLoading('container');
try {
const dados = await fetch('/api/itens')
.then(r => r.json());
renderizarLista(dados, 'container');
} catch (e) {
mostrarErro('container', 'Não foi possível carregar os dados.');
}
}