Frontend - HTML, CSS y Manipulación del DOM
🚀 Clases en Vivo — Inicio: Domingo 6 de Septiembre ($200 MXN / clase). Aprende a programar desde cero con acompañamiento directo y construye el proyecto ShortURL.
En la Lección 5: Stacks Tecnológicos y Arquitectura (L5) diseñamos la arquitectura completa de ShortURL y creamos el prototipo algorítmico en memoria.
Ahora daremos vida a la primera gran capa de nuestro stack: el Frontend. Los usuarios no interactúan con una aplicación a través de la terminal o de la consola de desarrollo; necesitan una interfaz gráfica atractiva, intuitiva, limpia y que se adapte perfectamente a cualquier pantalla, desde un monitor de escritorio hasta un teléfono inteligente.
En esta sesión intensiva de 4 horas, construiremos la interfaz gráfica completa de ShortURL utilizando la trinidad clásica de la web: HTML5 para la estructura, CSS3 con Flexbox para el diseño visual, y JavaScript para conectar los eventos del usuario con el Document Object Model (DOM).
🗺️ Estructura de la Sesión
Cada una de nuestras sesiones semanales sigue una metodología pedagógica dividida en 5 fases:
Estructura de la Sesión├── ☕ Fase 1: Revisión del Tema Anterior y Conclusiones├── 🎯 Fase 2: Introducción al Tema y Fundamentos├── 💻 Fase 3: Práctica Asistida en Vivo├── 🗣️ Fase 4: Exposición, Debate y Debugging└── 🏠 Fase 5: Reto Semanal y Práctica en Casa☕ Fase 1: Revisión del Tema Anterior y Conclusiones
Iniciamos la sesión conectando el prototipo lógico de la semana anterior con la experiencia de usuario.
1. Puesta en Común del Reto de la Lección 5
En la lección previa desarrollamos el “Prototipo en Memoria de ShortURL”, generando códigos aleatorios y validando URLs:
- Discusión en grupo: ¿Cómo manejaste el rechazo de URLs que no comenzaban con
http://ohttps://? - ¿Qué sentiste al ver la tabla de clics en la consola con
console.table()? - Si le pidiéramos a un amigo que use tu programa ahora mismo… ¿sabría cómo abrir la consola de desarrollador para escribir comandos de JavaScript?
2. El Puente Conceptual: La Interfaz de Usuario (UI)
El 99% de las personas que navegan por internet nunca han visto una línea de código. La misión del desarrollador Frontend es crear un “puente visual” para que cualquier persona pueda utilizar la lógica de nuestro software simplemente haciendo clic en botones y escribiendo en campos de texto.
Hoy construiremos los tres archivos que conformarán la interfaz pública de ShortURL:
index.html: El esqueleto de la página.styles.css: La ropa, colores y disposición espacial adaptable.app.js: El cerebro interactivo que escucha las acciones del usuario.
🎯 Fase 2: Introducción al Tema y Fundamentos Teóricos
Analicemos cómo colaboran las tres tecnologías del Frontend en el navegador.
1. HTML5 Semántico: Estructura con Significado
HTML (HyperText Markup Language) utiliza etiquetas para decirle al navegador qué tipo de información contiene cada elemento. El HTML moderno es semántico: usamos etiquetas que describen su propósito real:
{/* Estructura Semántica Básica */}<main class="contenedor-principal"> <header class="encabezado"> <h1>ShortURL</h1> <p>Acorta tus enlaces en un segundo</p> </header>
<form id="formulario-acortador"> <input type="url" placeholder="Pega tu enlace largo aquí..." required /> <button type="submit">Acortar Enlace</button> </form>
<section id="contenedor-resultado" class="oculto"> {/* Aquí aparecerá el enlace acortado con JavaScript */} </section></main>2. CSS3 Moderno y Flexbox: Centrado y Diseño Responsivo
CSS (Cascading Style Sheets) se encarga del aspecto visual. Durante muchos años centrar elementos en una página web era una pesadilla; hoy utilizamos CSS Flexbox, un sistema que organiza los elementos de forma elástica y perfecta:
/* Centrar la tarjeta en medio de la pantalla */body { margin: 0; min-height: 100vh; display: flex; justify-content: center; /* Centrado horizontal */ align-items: center; /* Centrado vertical */ font-family: system-ui, -apple-system, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);}
.tarjeta { background: #ffffff; padding: 2.5rem; border-radius: 1rem; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); width: 100%; max-width: 480px;}3. JavaScript y el DOM: El Sistema Nervioso
El DOM (Document Object Model) es la representación en memoria que el navegador hace de nuestro HTML. A través del DOM, JavaScript puede “ver”, escuchar y modificar cualquier elemento de la página:
[Usuario hace clic en "Acortar"] │ ▼[addEventListener("submit")] ──► [event.preventDefault()] ──► [Modificar DOM con resultado]El método salvavidas: event.preventDefault()
Por defecto, cuando envías un formulario HTML, el navegador intenta recargar la página completa. Para evitar que la página se reinicie y borre los datos, usamos event.preventDefault():
const formulario = document.getElementById("formulario-acortador");
formulario.addEventListener("submit", function(evento) { evento.preventDefault(); // ¡Detiene la recarga de la página! console.log("Formulario capturado limpiamente en JavaScript.");});🔍 Experimento en Vivo: Manipulando el DOM en Tiempo Real
Vamos a crear un elemento visual en cualquier página web directamente desde la consola:
- Abre cualquier página web en tu navegador y abre la consola (
F12). - Ejecuta este código para inyectar una tarjeta flotante:
const aviso = document.createElement("div");aviso.innerText = "🚀 ¡Hola desde el DOM de JavaScript!";aviso.style.position = "fixed";aviso.style.top = "20px";aviso.style.right = "20px";aviso.style.padding = "15px 25px";aviso.style.background = "#10b981";aviso.style.color = "white";aviso.style.borderRadius = "8px";aviso.style.fontWeight = "bold";aviso.style.zIndex = "99999";document.body.appendChild(aviso);
- Observa tu pantalla: Verás aparecer una tarjeta verde en la esquina superior derecha. ¡Acabas de modificar el DOM con código puro!
💻 Fase 3: Práctica Asistida en Vivo
Construiremos los 3 archivos de la interfaz gráfica de ShortURL.
🧩 Reto 1: Maquetación Semántica (index.html)
Objetivo: Crear el archivo base de la aplicación con estructura limpia y accesible.
<!DOCTYPE html><html lang="es"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>ShortURL | Acortador de Enlaces</title> <link rel="stylesheet" href="styles.css" /></head><body>
<main class="tarjeta"> <header class="tarjeta-header"> <div class="icono-logo">🔗</div> <h1>ShortURL</h1> <p>Pega un enlace largo para obtener uno corto y fácil de compartir.</p> </header>
<form id="form-acortar" class="formulario"> <div class="grupo-input"> <input type="url" id="input-url" placeholder="https://ejemplo.com/enlace-muy-largo..." required /> </div> <button type="submit" id="btn-acortar">Acortar Enlace</button> </form>
{/* Contenedor del resultado (inicialmente vacío) */} <section id="seccion-resultado" class="resultado-oculto"></section> </main>
{/* Script cargado al final del body */} <script src="app.js"></script></body></html>🧩 Reto 2: Estilos Visuales Modernos y Adaptables (styles.css)
Objetivo: Diseñar una interfaz limpia, centrada con Flexbox y con sombras suaves.
* { box-sizing: border-box; margin: 0; padding: 0;}
body { min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 1.5rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: linear-gradient(135deg, #1e1e2f 0%, #111119 100%); color: #333333;}
.tarjeta { background: #ffffff; width: 100%; max-width: 480px; padding: 2.5rem; border-radius: 1.25rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.35); text-align: center;}
.tarjeta-header .icono-logo { font-size: 3rem; margin-bottom: 0.5rem;}
.tarjeta-header h1 { font-size: 1.8rem; color: #1e1e2f; margin-bottom: 0.5rem;}
.tarjeta-header p { color: #6b7280; font-size: 0.95rem; margin-bottom: 2rem;}
.formulario { display: flex; flex-direction: column; gap: 1rem;}
.grupo-input input { width: 100%; padding: 0.9rem 1.2rem; font-size: 1rem; border: 2px solid #e5e7eb; border-radius: 0.75rem; outline: none; transition: border-color 0.2s;}
.grupo-input input:focus { border-color: #6366f1;}
button { background: #6366f1; color: #ffffff; font-size: 1rem; font-weight: 600; padding: 0.9rem; border: none; border-radius: 0.75rem; cursor: pointer; transition: background 0.2s, transform 0.1s;}
button:hover { background: #4f46e5;}
button:active { transform: scale(0.98);}
/* Estilos de la tarjeta de resultado */.resultado-oculto { display: none;}
.tarjeta-resultado { margin-top: 1.5rem; padding: 1.25rem; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 0.75rem; display: flex; flex-direction: column; gap: 0.75rem;}
.enlace-corto { color: #16a34a; font-weight: bold; font-size: 1.1rem; word-break: break-all;}
.btn-copiar { background: #16a34a;}
.btn-copiar:hover { background: #15803d;}🧩 Reto 3: Conexión Interactiva del DOM (app.js)
Objetivo: Interceptar el envío del formulario, leer el valor ingresado y generar un código corto simulado.
// 1. Seleccionamos los elementos del DOM que vamos a utilizarconst formulario = document.getElementById("form-acortar");const inputUrl = document.getElementById("input-url");const seccionResultado = document.getElementById("seccion-resultado");
// 2. Función auxiliar para generar un código corto de pruebafunction generarCodigoAleatorio(longitud = 6) { const caracteres = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; let codigo = ""; for (let i = 0; i < longitud; i++) { codigo += caracteres[Math.floor(Math.random() * caracteres.length)]; } return codigo;}
// 3. Escuchamos el evento "submit" del formularioformulario.addEventListener("submit", function(evento) { // Evitamos que la página se recargue evento.preventDefault();
const urlOriginal = inputUrl.value.trim(); const codigo = generarCodigoAleatorio(); const urlCorta = `https://short.ly/${codigo}`;
// Mostramos el resultado en pantalla renderizarResultado(urlCorta, urlOriginal);});🧩 Reto 4: Renderizado Dinámico y Botón “Copiar al Portapapeles”
Objetivo: Inyectar HTML dinámico con el enlace generado y permitir copiarlo con un clic usando la API moderna del navegador.
function renderizarResultado(urlCorta, urlOriginal) { // Construimos el HTML de la tarjeta de resultado seccionResultado.className = "tarjeta-resultado"; seccionResultado.innerHTML = ` <p style="font-size: 0.85rem; color: #6b7280;">¡Tu enlace acortado está listo!</p> <a href="${urlOriginal}" target="_blank" class="enlace-corto" id="texto-enlace">${urlCorta}</a> <button type="button" class="btn-copiar" id="btn-copiar">📋 Copiar al Portapapeles</button> `;
// Agregamos interactividad al botón de copiar const btnCopiar = document.getElementById("btn-copiar");
btnCopiar.addEventListener("click", async function() { try { // Usamos la API del Portapapeles del navegador await navigator.clipboard.writeText(urlCorta); btnCopiar.innerText = "✅ ¡Copiado con éxito!";
// Restauramos el texto original después de 2 segundos setTimeout(() => { btnCopiar.innerText = "📋 Copiar al Portapapeles"; }, 2000); } catch (error) { console.error("No se pudo copiar:", error); } });}🗣️ Fase 4: Exposición de Resultados, Debate y Debugging
Probamos los archivos en el navegador, revisamos los proyectos de los estudiantes y analizamos los errores típicos de Frontend.
🐞 Las 4 Trampas Clásicas de Frontend
1. Olvidar event.preventDefault()
- El síntoma: Haces clic en “Acortar”, la pantalla parpadea medio segundo y todo el formulario vuelve a quedar en blanco sin mostrar el enlace.
- La causa: El navegador envió una petición HTTP tradicional y recargó la página entera.
- La solución: La primera línea de tu listener de formulario siempre debe ser
evento.preventDefault().
2. Colocar la etiqueta <script> en el <head> sin defer
- El síntoma:
TypeError: Cannot read properties of null (reading 'addEventListener'). - La causa: El navegador ejecutó el archivo JavaScript antes de haber leído el HTML del formulario, por lo que
document.getElementById()devolviónull. - La solución: Coloca tu etiqueta
<script src="app.js"></script>justo antes de cerrar</body>.
3. Confundir class con id al seleccionar en JavaScript
- El error: Escribir
document.getElementById("formulario")cuando en el HTML pusiste<form class="formulario">. - Regla:
getElementByIdbusca exclusivamente atributosid="". Para clases se usadocument.querySelector(".formulario").
4. Olvidar el <meta name="viewport">
- El síntoma: La página se ve perfecta en tu computadora, pero al abrirla en el celular se ve minúscula y desproporcionada.
- La solución: Incluir siempre la etiqueta meta de viewport en el
<head>.
🏠 Fase 5: Definición de Práctica en Casa / Reto Semanal
Mejorarás la experiencia de usuario (UX) de ShortURL con validación visual en tiempo real.
🏆 Proyecto Semanal: “El Validador Visual en Tiempo Real”
Descripción del Problema: Los usuarios a veces cometen errores al escribir enlaces (por ejemplo, escriben texto sin formato o enlaces incompletos). Vamos a guiar al usuario visualmente mientras escribe.
Especificaciones Técnicas a Implementar:
-
Validación mientras escribe (Evento
input):- Agrega un
addEventListener("input", ...)sobreinputUrl. - Si el texto ingresado no comienza con
"http://"o"https://", cambia el borde del input a color rojo (#ef4444) y muestra un mensaje pequeño en texto rojo: “⚠️ El enlace debe comenzar con http:// o https://”. - Si el enlace es válido, cambia el borde a color verde (
#22c55e) y oculta el mensaje de error.
- Agrega un
-
Deshabilitar el botón si la URL no es válida:
- Si el texto no es una URL válida, añade la propiedad
btnAcortar.disabled = true;y reduce su opacidad en CSS. - En cuanto sea válida, reactiva el botón
btnAcortar.disabled = false;.
- Si el texto no es una URL válida, añade la propiedad
-
Limpieza automática:
- Cuando se genere el enlace corto exitosamente, limpia el campo de texto
inputUrl.value = "";para que el usuario pueda ingresar otro de inmediato.
- Cuando se genere el enlace corto exitosamente, limpia el campo de texto
📚 Recursos y Videos Recomendados
Para profundizar en desarrollo Frontend y manipulación del DOM:
🚀 Conclusión y Próximos Pasos
¡Excelente! Ya tenemos una interfaz gráfica moderna, atractiva, responsiva y conectada con eventos de JavaScript.
Sin embargo, en este momento los enlaces se generan solo en la memoria de nuestro navegador. Si otra persona intenta entrar a https://short.ly/a8B9x, ¡la página no existe en internet!
En la próxima sesión daremos el gran salto al lado del servidor: Lección 7: Backend - Servidor Web & API REST con Node.js y Express (L7), donde crearemos nuestro primer servidor real para procesar peticiones y redirigir visitantes de todo el mundo.
🚀 Clases en Vivo — Inicio: Domingo 6 de Septiembre ($200 MXN / clase). Aprende a programar desde cero con acompañamiento directo y construye el proyecto ShortURL.