Fundamentos de desarrollo web: HTML, CSS, JavaScript, manipulación del DOM y diseño responsivo
Este artículo es una introducción exhaustiva a los fundamentos del desarrollo web, incluyendo HTML5, CSS3, JavaScript ES6+, manipulación del DOM y diseño responsivo con ejemplos prácticos.
En pocas palabras
El desarrollo web se construye sobre tres tecnologías centrales: HTML proporciona la estructura, CSS el estilo y JavaScript la interactividad. El diseño responsivo garantiza que la presentación sea óptima en todos los dispositivos.
Descripción técnica resumida
Desarrollo web es la creación de aplicaciones web mediante la combinación de lenguajes de marcado, estilos y programación.
Tecnologías centrales:
HTML5 (HyperText Markup Language)
- Estructura: Marcado semántico de contenidos
- Elementos: Header, nav, main, section, article, footer
- Formularios: Tipos de entrada, validación, accesibilidad
- Multimedia: Audio, video, canvas, SVG
CSS3 (Cascading Style Sheets)
- Estilos: Colores, fuentes, diseño, animaciones
- Diseño: Flexbox, grid, posicionamiento, diseño responsivo
- Pseudo-clases: :hover, :active, :focus, :nth-child
- Media queries: Diseño responsivo, puntos de ruptura
JavaScript ES6+
- Interactividad: Manipulación del DOM, manejo de eventos
- Características: Arrow functions, template literals, destructuring
- API del DOM: Seleccionar elementos, manipularlos, eventos
- Asincronía: Promises, async/await, Fetch API
Puntos clave para el examen
- HTML5: Lenguaje de marcado semántico para contenidos web
- CSS3: Lenguaje de hojas de estilo para diseño y diseño
- JavaScript: Lenguaje de programación para interactividad web
- DOM: Document Object Model para manipulación dinámica
- Diseño responsivo: Adaptación a diferentes tamaños de pantalla
- Media queries: Condiciones CSS para propiedades de dispositivos
- Flexbox/Grid: Técnicas modernas de diseño
- Relevancia IHK: Base para todas las profesiones en desarrollo web
Componentes clave
- HTML5: Estructura semántica y contenido
- CSS3: Presentación visual y diseño
- JavaScript: Funcionalidad dinámica
- DOM: Interfaz programable
- Diseño responsivo: Compatibilidad entre dispositivos
- Accesibilidad: Desarrollo web accesible
- Rendimiento: Velocidades de carga y optimización
- Mejores prácticas: Estándares modernos de desarrollo
Ejemplos prácticos
1. Estructura básica HTML5
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Aplicación web moderna con HTML5, CSS3 y JavaScript">
<title>Demo de desarrollo web</title>
<!-- Incluir CSS -->
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<!-- Open Graph Meta Tags -->
<meta property="og:title" content="Demo de desarrollo web">
<meta property="og:description" content="Aplicación web moderna">
<meta property="og:type" content="website">
</head>
<body>
<!-- Skip Navigation para accesibilidad -->
<a href="#main-content" class="skip-link">Ir al contenido principal</a>
<!-- Header con navegación -->
<header class="header">
<nav class="nav" aria-label="Navegación principal">
<div class="nav-container">
<div class="nav-brand">
<a href="#" class="brand-link">
<img src="logo.svg" alt="Logo" class="brand-logo">
<span class="brand-text">WebDev</span>
</a>
</div>
<button class="nav-toggle" aria-expanded="false" aria-controls="nav-menu">
<span class="hamburger"></span>
<span class="visually-hidden">Menú</span>
</button>
<ul id="nav-menu" class="nav-menu">
<li><a href="#home" class="nav-link">Inicio</a></li>
<li><a href="#features" class="nav-link">Características</a></li>
<li><a href="#contact" class="nav-link">Contacto</a></li>
</ul>
</div>
</nav>
</header>
<!-- Contenido principal -->
<main id="main-content" class="main">
<!-- Sección hero -->
<section class="hero" id="home">
<div class="container">
<h1 class="hero-title">Desarrollo web moderno</h1>
<p class="hero-subtitle">HTML5, CSS3 y JavaScript en acción</p>
<div class="hero-actions">
<button class="btn btn-primary" data-action="start">Comenzar</button>
<a href="#features" class="btn btn-secondary">Saber más</a>
</div>
<!-- Demo interactiva -->
<div class="demo-section">
<div class="demo-container">
<canvas id="demo-canvas" width="400" height="200"></canvas>
<div class="demo-controls">
<button class="demo-btn" data-action="clear">Limpiar</button>
<button class="demo-btn" data-action="animate">Animar</button>
<input type="color" id="color-picker" value="#3b82f6">
</div>
</div>
</div>
</div>
</section>
<!-- Sección de características -->
<section class="features" id="features">
<div class="container">
<h2 class="section-title">Características</h2>
<div class="features-grid">
<article class="feature-card">
<div class="feature-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z"/>
</svg>
</div>
<h3 class="feature-title">Seguridad</h3>
<p class="feature-description">Estándares de seguridad modernos y mejores prácticas</p>
</article>
<article class="feature-card">
<div class="feature-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
<line x1="8" y1="21" x2="16" y2="21"/>
<line x1="12" y1="17" x2="12" y2="21"/>
</svg>
</div>
<h3 class="feature-title">Responsivo</h3>
<p class="feature-description">Presentación óptima en todos los dispositivos</p>
</article>
<article class="feature-card">
<div class="feature-icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
</svg>
</div>
<h3 class="feature-title">Rendimiento</h3>
<p class="feature-description">Tiempos de carga rápidos e interacción fluida</p>
</article>
</div>
</div>
</section>
<!-- Formulario de contacto -->
<section class="contact" id="contact">
<div class="container">
<h2 class="section-title">Contacto</h2>
<form class="contact-form" novalidate>
<div class="form-group">
<label for="name" class="form-label">Nombre *</label>
<input type="text" id="name" name="name" class="form-input" required>
<span class="form-error" id="name-error">Por favor ingrese su nombre</span>
</div>
<div class="form-group">
<label for="email" class="form-label">Correo electrónico *</label>
<input type="email" id="email" name="email" class="form-input" required>
<span class="form-error" id="email-error">Por favor ingrese un correo válido</span>
</div>
<div class="form-group">
<label for="message" class="form-label">Mensaje</label>
<textarea id="message" name="message" class="form-textarea" rows="4"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Enviar</button>
<button type="reset" class="btn btn-secondary">Limpiar</button>
</div>
</form>
</div>
</section>
</main>
<!-- Footer -->
<footer class="footer">
<div class="container">
<p>© 2024 Demo de desarrollo web. Todos los derechos reservados.</p>
<div class="footer-links">
<a href="#impressum">Aviso legal</a>
<a href="#datenschutz">Privacidad</a>
</div>
</div>
</footer>
<!-- Incluir JavaScript -->
<script src="script.js"></script>
</body>
</html>
2. CSS3 con Responsive Design
/* CSS Reset y estilos base */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* CSS Custom Properties (Variables) */
:root {
/* Colores */
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-primary-light: #60a5fa;
--color-secondary: #64748b;
--color-success: #10b981;
--color-error: #ef4444;
--color-warning: #f59e0b;
/* Neutros */
--color-white: #ffffff;
--color-gray-50: #f8fafc;
--color-gray-100: #f1f5f9;
--color-gray-900: #0f172a;
/* Tipografía */
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
/* Espaciado */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
--spacing-2xl: 3rem;
/* Radio de borde */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-full: 9999px;
/* Sombras */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1);
/* Transiciones */
--transition-fast: 150ms ease-in-out;
--transition-normal: 300ms ease-in-out;
--transition-slow: 500ms ease-in-out;
}
/* Estilos base */
html {
font-size: 16px;
scroll-behavior: smooth;
}
body {
font-family: var(--font-family);
font-size: var(--font-size-base);
line-height: 1.6;
color: var(--color-gray-900);
background-color: var(--color-white);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Clases auxiliares */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--color-primary);
color: var(--color-white);
padding: var(--spacing-sm) var(--spacing-md);
text-decoration: none;
border-radius: var(--radius-md);
z-index: 100;
transition: top var(--transition-fast);
}
.skip-link:focus {
top: var(--spacing-sm);
}
/* Encabezado y navegación */
.header {
background-color: var(--color-white);
box-shadow: var(--shadow-sm);
position: sticky;
top: 0;
z-index: 50;
}
.nav-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md) 0;
}
.brand-link {
display: flex;
align-items: center;
gap: var(--spacing-sm);
text-decoration: none;
color: var(--color-gray-900);
font-weight: 600;
transition: color var(--transition-fast);
}
.brand-link:hover {
color: var(--color-primary);
}
.brand-logo {
width: 32px;
height: 32px;
}
.nav-toggle {
display: none;
background: none;
border: none;
padding: var(--spacing-sm);
cursor: pointer;
}
.hamburger {
display: block;
width: 24px;
height: 2px;
background-color: var(--color-gray-900);
position: relative;
transition: background-color var(--transition-fast);
}
.hamburger::before,
.hamburger::after {
content: '';
position: absolute;
width: 24px;
height: 2px;
background-color: var(--color-gray-900);
transition: transform var(--transition-fast);
}
.hamburger::before {
top: -8px;
}
.hamburger::after {
top: 8px;
}
.nav-toggle[aria-expanded="true"] .hamburger {
background-color: transparent;
}
.nav-toggle[aria-expanded="true"] .hamburger::before {
transform: rotate(45deg);
top: 0;
}
.nav-toggle[aria-expanded="true"] .hamburger::after {
transform: rotate(-45deg);
top: 0;
}
.nav-menu {
display: flex;
list-style: none;
gap: var(--spacing-lg);
}
.nav-link {
text-decoration: none;
color: var(--color-gray-700);
font-weight: 500;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--radius-md);
transition: all var(--transition-fast);
}
.nav-link:hover {
color: var(--color-primary);
background-color: var(--color-gray-50);
}
/* Sección hero */
.hero {
background: linear-gradient(135deg, var(--color-gray-50) 0%, var(--color-white) 100%);
padding: var(--spacing-2xl) 0;
text-align: center;
}
.hero-title {
font-size: var(--font-size-4xl);
font-weight: 700;
color: var(--color-gray-900);
margin-bottom: var(--spacing-md);
line-height: 1.2;
}
.hero-subtitle {
font-size: var(--font-size-xl);
color: var(--color-secondary);
margin-bottom: var(--spacing-xl);
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.hero-actions {
display: flex;
gap: var(--spacing-md);
justify-content: center;
margin-bottom: var(--spacing-2xl);
}
/* Botones */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-md) var(--spacing-lg);
font-size: var(--font-size-base);
font-weight: 600;
text-decoration: none;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
min-width: 120px;
}
.btn-primary {
background-color: var(--color-primary);
color: var(--color-white);
}
.btn-primary:hover {
background-color: var(--color-primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.btn-secondary {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.btn-secondary:hover {
background-color: var(--color-gray-200);
transform: translateY(-2px);
}
/* Sección demo */
.demo-section {
margin-top: var(--spacing-2xl);
}
.demo-container {
background-color: var(--color-white);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
max-width: 500px;
margin: 0 auto;
}
#demo-canvas {
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-md);
width: 100%;
height: auto;
display: block;
margin-bottom: var(--spacing-md);
}
.demo-controls {
display: flex;
gap: var(--spacing-sm);
flex-wrap: wrap;
align-items: center;
}
.demo-btn {
padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--color-gray-100);
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: var(--font-size-sm);
transition: all var(--transition-fast);
}
.demo-btn:hover {
background-color: var(--color-gray-200);
}
#color-picker {
width: 50px;
height: 36px;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
}
/* Sección características */
.features {
padding: var(--spacing-2xl) 0;
background-color: var(--color-gray-50);
}
.section-title {
font-size: var(--font-size-3xl);
font-weight: 700;
text-align: center;
margin-bottom: var(--spacing-2xl);
color: var(--color-gray-900);
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-xl);
}
.feature-card {
background-color: var(--color-white);
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
text-align: center;
transition: transform var(--transition-normal), box-shadow var(--transition-normal);
}
.feature-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-xl);
}
.feature-icon {
display: flex;
justify-content: center;
margin-bottom: var(--spacing-lg);
color: var(--color-primary);
}
.feature-title {
font-size: var(--font-size-xl);
font-weight: 600;
margin-bottom: var(--spacing-md);
color: var(--color-gray-900);
}
.feature-description {
color: var(--color-secondary);
line-height: 1.6;
}
/* Formulario de contacto */
.contact {
padding: var(--spacing-2xl) 0;
}
.contact-form {
max-width: 600px;
margin: 0 auto;
}
.form-group {
margin-bottom: var(--spacing-lg);
}
.form-label {
display: block;
font-weight: 600;
margin-bottom: var(--spacing-sm);
color: var(--color-gray-900);
}
.form-input,
.form-textarea {
width: 100%;
padding: var(--spacing-md);
border: 2px solid var(--color-gray-200);
border-radius: var(--radius-md);
font-size: var(--font-size-base);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.form-input:focus,
.form-textarea:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgb(59 130 246 / 0.1);
}
.form-input:invalid,
.form-textarea:invalid {
border-color: var(--color-error);
}
.form-error {
display: none;
color: var(--color-error);
font-size: var(--font-size-sm);
margin-top: var(--spacing-xs);
}
.form-input:invalid ~ .form-error {
display: block;
}
.form-actions {
display: flex;
gap: var(--spacing-md);
justify-content: flex-end;
}
/* Pie de página */
.footer {
background-color: var(--color-gray-900);
color: var(--color-gray-100);
padding: var(--spacing-xl) 0;
text-align: center;
}
.footer-links {
margin-top: var(--spacing-md);
display: flex;
gap: var(--spacing-lg);
justify-content: center;
}
.footer-links a {
color: var(--color-gray-400);
text-decoration: none;
transition: color var(--transition-fast);
}
.footer-links a:hover {
color: var(--color-white);
}
/* Responsive Design */
/* Enfoque Mobile First */
/* Tablets pequeños y teléfonos grandes */
@media (min-width: 640px) {
.container {
padding: 0 var(--spacing-lg);
}
.hero-title {
font-size: var(--font-size-5xl);
}
.features-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Tablets */
@media (min-width: 768px) {
.nav-toggle {
display: none;
}
.nav-menu {
display: flex;
}
.hero-actions {
flex-direction: row;
}
.demo-controls {
justify-content: center;
}
}
/* Desktop */
@media (min-width: 1024px) {
.hero {
padding: var(--spacing-3xl) 0;
}
.features-grid {
grid-template-columns: repeat(3, 1fr);
}
.form-actions {
justify-content: flex-start;
}
}
/* Desktop grande */
@media (min-width: 1280px) {
.container {
padding: 0 var(--spacing-xl);
}
.hero-title {
font-size: var(--font-size-6xl);
}
}
/* Soporte para modo oscuro */
@media (prefers-color-scheme: dark) {
:root {
--color-white: #0f172a;
--color-gray-50: #1e293b;
--color-gray-100: #334155;
--color-gray-900: #f8fafc;
--color-gray-700: #cbd5e1;
--color-gray-200: #475569;
}
.header {
background-color: var(--color-gray-50);
}
.hero {
background: linear-gradient(135deg, var(--color-gray-50) 0%, var(--color-white) 100%);
}
.features {
background-color: var(--color-white);
}
.btn-secondary {
background-color: var(--color-gray-200);
color: var(--color-gray-900);
}
.btn-secondary:hover {
background-color: var(--color-gray-100);
}
}
/* Estilos de impresión */
@media print {
.nav-toggle,
.hero-actions,
.demo-section,
.contact-form {
display: none;
}
.hero {
padding: var(--spacing-lg) 0;
}
body {
font-size: 12pt;
line-height: 1.4;
}
h1, h2, h3 {
page-break-after: avoid;
}
.feature-card {
break-inside: avoid;
}
}
/* Clases de animación */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.6s ease-out;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.pulse {
animation: pulse 2s infinite;
}
/* Estilos de enfoque para Accessibility */
.btn:focus,
.nav-link:focus,
.form-input:focus,
.form-textarea:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Modo de alto contraste */
@media (prefers-contrast: high) {
:root {
--color-primary: #0000ff;
--color-secondary: #000000;
--color-gray-900: #000000;
--color-gray-100: #ffffff;
}
.btn {
border: 2px solid currentColor;
}
.form-input,
.form-textarea {
border-width: 3px;
}
}
/* Movimiento reducido */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
3. JavaScript ES6+ con manipulación del DOM
// JavaScript moderno con características ES6+
// Seleccionar elementos del DOM
const elements = {
// Navegación
navToggle: document.querySelector('.nav-toggle'),
navMenu: document.querySelector('#nav-menu'),
navLinks: document.querySelectorAll('.nav-link'),
// Sección hero
heroTitle: document.querySelector('.hero-title'),
heroActions: document.querySelector('.hero-actions'),
// Demo
canvas: document.querySelector('#demo-canvas'),
demoButtons: document.querySelectorAll('.demo-btn'),
colorPicker: document.querySelector('#color-picker'),
// Formulario
contactForm: document.querySelector('.contact-form'),
formInputs: document.querySelectorAll('.form-input'),
// Características
featureCards: document.querySelectorAll('.feature-card')
};
// Gestión de estado
const state = {
isNavOpen: false,
currentColor: '#3b82f6',
isDrawing: false,
lastX: 0,
lastY: 0
};
// Funciones utilitarias
const utils = {
// Debounce para rendimiento
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// Throttle para eventos
throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
// Mostrar elemento con animación
animateIn(element, animation = 'fade-in') {
element.style.opacity = '0';
element.classList.add(animation);
// Forzar reflow
element.offsetHeight;
element.style.opacity = '1';
setTimeout(() => {
element.classList.remove(animation);
element.style.opacity = '';
}, 600);
},
// Desplazamiento suave
smoothScroll(target) {
const element = document.querySelector(target);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
},
// LocalStorage
saveToLocalStorage(key, data) {
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (error) {
console.warn('LocalStorage no disponible:', error);
}
},
getFromLocalStorage(key) {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch (error) {
console.warn('Error al leer LocalStorage:', error);
return null;
}
}
};
// Navegación
class Navigation {
constructor() {
this.init();
}
init() {
// Toggle de navegación móvil
if (elements.navToggle) {
elements.navToggle.addEventListener('click', this.toggleNav.bind(this));
}
// Enlaces de navegación
elements.navLinks.forEach(link => {
link.addEventListener('click', this.handleNavClick.bind(this));
});
// Cerrar al presionar Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && state.isNavOpen) {
this.closeNav();
}
});
// Cerrar al hacer clic fuera
document.addEventListener('click', (e) => {
if (state.isNavOpen && !elements.navMenu.contains(e.target) && !elements.navToggle.contains(e.target)) {
this.closeNav();
}
});
}
toggleNav() {
state.isNavOpen = !state.isNavOpen;
this.updateNavUI();
}
openNav() {
state.isNavOpen = true;
this.updateNavUI();
}
closeNav() {
state.isNavOpen = false;
this.updateNavUI();
}
updateNavUI() {
elements.navToggle.setAttribute('aria-expanded', state.isNavOpen);
elements.navMenu.classList.toggle('nav-open', state.isNavOpen);
// Bloquear scroll del body
document.body.style.overflow = state.isNavOpen ? 'hidden' : '';
}
handleNavClick(e) {
e.preventDefault();
const href = e.target.getAttribute('href');
if (href && href.startsWith('#')) {
utils.smoothScroll(href);
this.closeNav();
}
}
}
// Demo con Canvas
class CanvasDemo {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.init();
}
init() {
this.setupCanvas();
this.bindEvents();
this.drawInitialContent();
}
setupCanvas() {
// Soporte para pantallas de alta resolución
const dpr = window.devicePixelRatio || 1;
const rect = this.canvas.getBoundingClientRect();
this.canvas.width = rect.width * dpr;
this.canvas.height = rect.height * dpr;
this.ctx.scale(dpr, dpr);
this.canvas.style.width = rect.width + 'px';
this.canvas.style.height = rect.height + 'px';
}
bindEvents() {
// Eventos del ratón
this.canvas.addEventListener('mousedown', this.startDrawing.bind(this));
this.canvas.addEventListener('mousemove', this.draw.bind(this));
this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this));
this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this));
// Eventos táctiles
this.canvas.addEventListener('touchstart', this.handleTouch.bind(this));
this.canvas.addEventListener('touchmove', this.handleTouch.bind(this));
this.canvas.addEventListener('touchend', this.stopDrawing.bind(this));
// Botones de demostración
elements.demoButtons.forEach(btn => {
btn.addEventListener('click', this.handleDemoAction.bind(this));
});
// Selector de color
elements.colorPicker.addEventListener('change', (e) => {
state.currentColor = e.target.value;
});
// Redimensionamiento de ventana
window.addEventListener('resize', utils.debounce(() => {
this.setupCanvas();
this.drawInitialContent();
}, 250));
}
startDrawing(e) {
state.isDrawing = true;
const rect = this.canvas.getBoundingClientRect();
state.lastX = e.clientX - rect.left;
state.lastY = e.clientY - rect.top;
}
draw(e) {
if (!state.isDrawing) return;
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.ctx.beginPath();
this.ctx.moveTo(state.lastX, state.lastY);
this.ctx.lineTo(x, y);
this.ctx.strokeStyle = state.currentColor;
this.ctx.lineWidth = 3;
this.ctx.lineCap = 'round';
this.ctx.stroke();
state.lastX = x;
state.lastY = y;
}
stopDrawing() {
state.isDrawing = false;
}
handleTouch(e) {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent(e.type === 'touchstart' ? 'mousedown' :
e.type === 'touchmove' ? 'mousemove' : 'mouseup', {
clientX: touch.clientX,
clientY: touch.clientY
});
this.canvas.dispatchEvent(mouseEvent);
}
handleDemoAction(e) {
const action = e.target.dataset.action;
switch (action) {
case 'clear':
this.clearCanvas();
break;
case 'animate':
this.animateShapes();
break;
}
}
clearCanvas() {
const rect = this.canvas.getBoundingClientRect();
this.ctx.clearRect(0, 0, rect.width, rect.height);
this.drawInitialContent();
}
drawInitialContent() {
const rect = this.canvas.getBoundingClientRect();
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Texto de bienvenida
this.ctx.font = '20px Inter';
this.ctx.fillStyle = state.currentColor;
this.ctx.textAlign = 'center';
this.ctx.fillText('¡Dibuja aquí!', centerX, centerY);
}
animateShapes() {
const rect = this.canvas.getBoundingClientRect();
let x = 0;
let y = rect.height / 2;
let dx = 2;
let dy = 1;
let hue = 0;
const animate = () => {
// Limpiar rastro
this.ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
this.ctx.fillRect(0, 0, rect.width, rect.height);
// Dibujar círculo
this.ctx.beginPath();
this.ctx.arc(x, y, 20, 0, Math.PI * 2);
this.ctx.fillStyle = `hsl(${hue}, 70%, 50%)`;
this.ctx.fill();
// Actualizar posición
x += dx;
y += dy;
hue += 2;
// Rebotar en las paredes
if (x > rect.width - 20 || x < 20) dx = -dx;
if (y > rect.height - 20 || y < 20) dy = -dy;
// Continuar animación
if (hue < 360) {
requestAnimationFrame(animate);
}
};
animate();
}
}
// Validación de formularios
class FormValidator {
constructor(form) {
this.form = form;
this.init();
}
init() {
this.form.addEventListener('submit', this.handleSubmit.bind(this));
// Validación en tiempo real
elements.formInputs.forEach(input => {
input.addEventListener('blur', () => this.validateField(input));
input.addEventListener('input', () => this.clearError(input));
});
}
handleSubmit(e) {
e.preventDefault();
if (this.validateForm()) {
this.submitForm();
} else {
this.showFirstError();
}
}
validateForm() {
let isValid = true;
elements.formInputs.forEach(input => {
if (!this.validateField(input)) {
isValid = false;
}
});
return isValid;
}
validateField(field) {
const value = field.value.trim();
const type = field.type;
const isRequired = field.hasAttribute('required');
let isValid = true;
// Validación de campo obligatorio
if (isRequired && !value) {
this.showError(field, 'Este campo es obligatorio');
isValid = false;
}
// Validación de email
if (type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
this.showError(field, 'Por favor ingresa una dirección de correo válida');
isValid = false;
}
}
// Validación de nombre
if (field.id === 'name' && value) {
if (value.length < 2) {
this.showError(field, 'El nombre debe tener al menos 2 caracteres');
isValid = false;
}
}
if (isValid) {
this.clearError(field);
}
return isValid;
}
showError(field, message) {
field.classList.add('error');
const errorElement = document.getElementById(`${field.id}-error`);
if (errorElement) {
errorElement.textContent = message;
errorElement.style.display = 'block';
}
}
clearError(field) {
field.classList.remove('error');
const errorElement = document.getElementById(`${field.id}-error`);
if (errorElement) {
errorElement.style.display = 'none';
}
}
showFirstError() {
const firstError = this.form.querySelector('.form-input.error');
if (firstError) {
firstError.focus();
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
submitForm() {
const formData = new FormData(this.form);
const data = Object.fromEntries(formData);
// Simular llamada a API
this.showLoading();
setTimeout(() => {
this.showSuccess();
this.form.reset();
}, 1500);
}
showLoading() {
const submitBtn = this.form.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = 'Enviando...';
}
showSuccess() {
const submitBtn = this.form.querySelector('button[type="submit"]');
submitBtn.disabled = false;
submitBtn.textContent = '¡Enviado!';
submitBtn.classList.add('success');
setTimeout(() => {
submitBtn.textContent = 'Enviar';
submitBtn.classList.remove('success');
}, 3000);
// Mostrar mensaje de éxito
this.showMessage('¡Mensaje enviado correctamente!', 'success');
}
showMessage(text, type = 'info') {
const message = document.createElement('div');
message.className = `message message-${type}`;
message.textContent = text;
this.form.appendChild(message);
utils.animateIn(message);
setTimeout(() => {
message.remove();
}, 5000);
}
}
// Intersection Observer para animaciones
class ScrollAnimations {
constructor() {
this.init();
}
init() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.animateElement(entry.target);
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observar elementos
elements.featureCards.forEach(card => {
observer.observe(card);
});
// Animación hero
if (elements.heroTitle) {
utils.animateIn(elements.heroTitle);
}
if (elements.heroActions) {
setTimeout(() => {
utils.animateIn(elements.heroActions);
}, 300);
}
}
animateElement(element) {
utils.animateIn(element);
// Animación de pulso para tarjetas de características
if (element.classList.contains('feature-card')) {
setTimeout(() => {
element.classList.add('pulse');
setTimeout(() => {
element.classList.remove('pulse');
}, 2000);
}, 1000);
}
}
}
// Inicialización de la aplicación
class App {
constructor() {
this.modules = {};
this.init();
}
init() {
// Verificar si el DOM está listo
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.start());
} else {
this.start();
}
}
start() {
console.log('🚀 Demo de desarrollo web iniciada');
// Inicializar módulos
this.modules.navigation = new Navigation();
if (elements.canvas) {
this.modules.canvas = new CanvasDemo(elements.canvas);
}
if (elements.contactForm) {
this.modules.form = new FormValidator(elements.contactForm);
}
this.modules.scrollAnimations = new ScrollAnimations();
// Monitoreo de rendimiento
this.initPerformanceMonitoring();
// Registro de Service Worker
this.registerServiceWorker();
}
initPerformanceMonitoring() {
// Rendimiento de carga de página
window.addEventListener('load', () => {
const perfData = performance.getEntriesByType('navigation')[0];
const loadTime = perfData.loadEventEnd - perfData.loadEventStart;
console.log(`⚡ Página cargada en ${loadTime}ms`);
// Advertencia para tiempos de carga lentos
if (loadTime > 3000) {
console.warn('⚠️ Se detectó una carga lenta');
}
});
// Largest Contentful Paint
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log(`🎨 Largest Contentful Paint: ${lastEntry.startTime}ms`);
}).observe({ entryTypes: ['largest-contentful-paint'] });
}
async registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('✅ Service Worker registrado:', registration.scope);
} catch (error) {
console.log('❌ Falló el registro del Service Worker:', error);
}
}
}
}
// Iniciar aplicación
const app = new App();
// Exportar para sistema de módulos
if (typeof module !== 'undefined' && module.exports) {
module.exports = { App, Navigation, CanvasDemo, FormValidator, ScrollAnimations, utils };
}
4. Service Worker para funcionalidad sin conexión
// sw.js - Service Worker para caché y funcionalidad sin conexión
const CACHE_NAME = 'webdev-demo-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/script.js',
'/logo.svg',
'/favicon.svg'
];
// Instalación del Service Worker
self.addEventListener('install', (event) => {
console.log('🔧 Service Worker installing...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('📦 Caching app shell');
return cache.addAll(urlsToCache);
})
.then(() => {
console.log('✅ App shell cached successfully');
return self.skipWaiting();
})
);
});
// Activación del Service Worker
self.addEventListener('activate', (event) => {
console.log('🔄 Service Worker activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
console.log('🗑️ Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(() => {
console.log('✅ Service Worker activated');
return self.clients.claim();
})
);
});
// Eventos Fetch - Network First con fallback a caché
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Solo para solicitudes HTTP/HTTPS
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return;
}
event.respondWith(
caches.match(request)
.then((response) => {
// Cache Hit - devolver respuesta en caché
if (response) {
console.log('📋 Serving from cache:', request.url);
return response;
}
// Solicitud a la red
console.log('🌐 Fetching from network:', request.url);
return fetch(request)
.then((response) => {
// Validar si la respuesta es correcta
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clonar respuesta para caché
const responseToCache = response.clone();
// Guardar respuestas exitosas en caché
if (shouldCache(request)) {
caches.open(CACHE_NAME)
.then((cache) => {
console.log('💾 Caching new resource:', request.url);
cache.put(request, responseToCache);
});
}
return response;
})
.catch(() => {
// La red falló, intentar caché
console.log('❌ Network failed, trying cache for:', request.url);
return caches.match(request);
});
})
);
});
// Función auxiliar para determinar si una solicitud debe almacenarse
function shouldCache(request) {
const url = new URL(request.url);
// No cachear llamadas API o recursos externos
if (url.pathname.startsWith('/api/') || url.origin !== self.location.origin) {
return false;
}
// Cachear recursos estáticos
const staticExtensions = ['.css', '.js', '.svg', '.png', '.jpg', '.jpeg', '.webp'];
const hasStaticExtension = staticExtensions.some(ext => url.pathname.endsWith(ext));
return hasStaticExtension || url.pathname === '/';
}
// Background Sync para acciones sin conexión
self.addEventListener('sync', (event) => {
if (event.tag === 'background-sync') {
console.log('🔄 Background sync triggered');
event.waitUntil(
// Aquí podrían sincronizarse acciones realizadas sin conexión
new Promise((resolve) => {
setTimeout(() => {
console.log('✅ Background sync completed');
resolve();
}, 1000);
})
);
}
});
// Notificaciones Push
self.addEventListener('push', (event) => {
console.log('📨 Push message received');
const options = {
body: event.data.text(),
icon: '/logo.svg',
badge: '/favicon.svg',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{
action: 'explore',
title: 'Explore',
icon: '/images/checkmark.png'
},
{
action: 'close',
title: 'Close',
icon: '/images/xmark.png'
}
]
};
event.waitUntil(
self.registration.showNotification('WebDev Demo', options)
);
});
// Manejo de clics en notificaciones
self.addEventListener('notificationclick', (event) => {
console.log('🔔 Notification clicked');
event.notification.close();
if (event.action === 'explore') {
event.waitUntil(
clients.openWindow('/')
);
}
});
Resumen de elementos semánticos de HTML5
| Elemento | Descripción | Uso |
|---|---|---|
| header | Encabezado | Logo, navegación |
| nav | Navegación | Menú, enlaces |
| main | Contenido principal | Contenido primario |
| section | Sección | Bloques temáticos |
| article | Artículo | Contenidos independientes |
| aside | Barra lateral | Información adicional |
| footer | Pie de página | Copyright, enlaces |
Técnicas de layout CSS3
Flexbox
.container {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.item {
flex: 1 1 300px;
margin: 1rem;
}
Grid
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
Container Queries
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
Características de JavaScript ES6+
Arrow Functions
const add = (a, b) => a + b;
const users = data.map(item => item.name);
Template Literals
const message = `Hola ${name}, tienes ${count} mensajes`;
Destructuring
const {name, email} = user;
const [first, second] = array;
Spread/Rest
const newArray = [...oldArray, newItem];
const sum = (...numbers) => numbers.reduce((a, b) => a + b);
Async/Await
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
}
}
Puntos de Quiebre Responsive
/* Mobile First Approach */
/* Small Phones */
@media (min-width: 320px) { }
/* Large Phones */
@media (min-width: 480px) { }
/* Tablets */
@media (min-width: 768px) { }
/* Desktop */
@media (min-width: 1024px) { }
/* Large Desktop */
@media (min-width: 1200px) { }
Métodos de Manipulación del DOM
| Método | Descripción | Ejemplo |
|---|---|---|
| querySelector | Seleccionar elemento | document.querySelector('.class') |
| createElement | Crear elemento | document.createElement('div') |
| appendChild | Agregar elemento | parent.appendChild(child) |
| classList | Gestionar clases | element.classList.add('active') |
| addEventListener | Vincular evento | element.addEventListener('click', handler) |
| setAttribute | Establecer atributo | element.setAttribute('data-id', '123') |
Animaciones CSS3
Transiciones
.button {
transition: all 0.3s ease-in-out;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
Keyframes
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.element {
animation: fadeIn 0.6s ease-out;
}
Optimización del Rendimiento Web
Lazy Loading
// Images
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
Code Splitting
// Dynamic Import
const loadModule = async () => {
const module = await import('./heavy-module.js');
module.doSomething();
};
Mejores Prácticas de Accesibilidad
HTML Semántico
<nav aria-label="Navegación Principal">
<ul>
<li><a href="#" aria-current="page">Home</a></li>
<li><a href="#">About</a></li>
</ul>
</nav>
Atributos ARIA
<button aria-expanded="false" aria-controls="menu">
Menú
</button>
<div id="menu" hidden>
<!-- Menu content -->
</div>
Navegación por Teclado
.focusable:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
Ventajas y Desventajas
Ventajas del desarrollo web moderno
- Multiplataforma: Un código para todos los dispositivos
- Responsive: Presentación óptima en cualquier lugar
- Interactivo: Experiencias de usuario enriquecidas
- Amigable con SEO: Optimizado para motores de búsqueda
- Accesible: Soporte sin barreras
Desventajas
- Complejidad: Muchas tecnologías que aprender
- Compatibilidad de navegadores: Soporte variable
- Rendimiento: Requiere optimización
- Seguridad: Necesita mecanismos de protección
Preguntas de Examen Frecuentes
-
¿Cuál es la diferencia entre HTML, CSS y JavaScript? HTML estructura el contenido, CSS define la apariencia, JavaScript habilita la interactividad.
-
Explica el diseño responsive. Adaptación de la presentación a diferentes tamaños de pantalla mediante Media Queries y layouts flexibles.
-
¿Qué es el DOM y cómo se manipula? El DOM es la representación programable del documento HTML. Se manipula mediante métodos JavaScript como querySelector, createElement, appendChild.
-
¿Cuándo usar Flexbox versus Grid? Flexbox para layouts unidimensionales (filas o columnas), Grid para layouts bidimensionales.
Recursos Principales
- https://developer.mozilla.org/es/docs/Web
- https://www.w3.org/TR/html5/
- https://www.w3.org/Style/CSS/
Lecturas Recomendadas: Desarrollo Web
Keine Bücher für Kategorie "web-development" gefunden.



