Skip to content
IRC-CodingIRC-Coding
Fundamentos desarrollo webHTML CSS JavaScriptDOM ManipulationResponsive DesignFrontend

Fundamentos de Desarrollo Web: HTML, CSS y JavaScript

Aprende HTML5, CSS3, JavaScript ES6+, manipulación del DOM y diseño responsive. Tecnologías web modernas con ejemplos prácticos.

S

schutzgeist

22 min read
Fundamentos de Desarrollo Web: HTML, CSS y JavaScript

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

  1. HTML5: Estructura semántica y contenido
  2. CSS3: Presentación visual y diseño
  3. JavaScript: Funcionalidad dinámica
  4. DOM: Interfaz programable
  5. Diseño responsivo: Compatibilidad entre dispositivos
  6. Accesibilidad: Desarrollo web accesible
  7. Rendimiento: Velocidades de carga y optimización
  8. 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>&copy; 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

ElementoDescripciónUso
headerEncabezadoLogo, navegación
navNavegaciónMenú, enlaces
mainContenido principalContenido primario
sectionSecciónBloques temáticos
articleArtículoContenidos independientes
asideBarra lateralInformación adicional
footerPie de páginaCopyright, 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étodoDescripciónEjemplo
querySelectorSeleccionar elementodocument.querySelector('.class')
createElementCrear elementodocument.createElement('div')
appendChildAgregar elementoparent.appendChild(child)
classListGestionar claseselement.classList.add('active')
addEventListenerVincular eventoelement.addEventListener('click', handler)
setAttributeEstablecer atributoelement.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>
.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

  1. ¿Cuál es la diferencia entre HTML, CSS y JavaScript? HTML estructura el contenido, CSS define la apariencia, JavaScript habilita la interactividad.

  2. Explica el diseño responsive. Adaptación de la presentación a diferentes tamaños de pantalla mediante Media Queries y layouts flexibles.

  3. ¿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.

  4. ¿Cuándo usar Flexbox versus Grid? Flexbox para layouts unidimensionales (filas o columnas), Grid para layouts bidimensionales.

Recursos Principales

  1. https://developer.mozilla.org/es/docs/Web
  2. https://www.w3.org/TR/html5/
  3. https://www.w3.org/Style/CSS/

Lecturas Recomendadas: Desarrollo Web

Keine Bücher für Kategorie "web-development" gefunden.

Volver al blog
Share:

Entradas relacionadas