addEventListener en JavaScript: Haz que tu página web responda a clics, teclas y más

Cómo detectar clics, teclas y formularios con JavaScript, con un dashboard interactivo como proyecto

Tienes un botón en tu página. Le pones un onclick="mifunción()" en el HTML, lo pruebas... y no pasa nada. O peor: funciona a veces y otras no, dependiendo de en qué orden carga el script. Aquí es donde la mayoría se atasca y no entiende por qué.

El problema casi siempre es el mismo: el código intenta acceder al botón antes de que exista en la página, o se usa la forma antigua de manejar eventos en vez de addEventListener. Este tutorial explica cómo funciona de verdad.

🤔 ¿Qué vamos a ver?

  • Qué es un evento y cómo funciona addEventListener
  • Los tipos de eventos más usados: click, keydown, submit, mouseover
  • El event object: la información que JavaScript te da en cada evento
  • Un dashboard interactivo completo como proyecto final
  • Los errores concretos que todo el mundo comete al empezar

Qué es un evento en JavaScript

Cuando alguien hace clic en un botón, pulsa una tecla o envía un formulario, el navegador genera un evento. JavaScript puede "escuchar" esos eventos y ejecutar código cuando ocurren.

Es como tener un portero en la puerta: no hace nada hasta que llaman, y cuando llaman ejecuta exactamente lo que le dijiste. No antes, no después.

La forma moderna de registrar esa escucha es addEventListener. Antes se usaba onclick directamente en el HTML, pero eso mezcla estructura con comportamiento y da problemas cuando tienes varios scripts. Esto me costó pillarlo al principio porque los tutoriales viejos siguen usando el método antiguo.

❌ Error común:

"¡Mi código no hace nada cuando hago clic!"

El problema más habitual: el script se ejecuta antes de que el HTML esté cargado. JavaScript intenta encontrar el botón, no existe todavía, y falla en silencio. Solución: pon el script al final del <body> o usa DOMContentLoaded.

Tu primer addEventListener

Vamos con el ejemplo más básico posible: un botón que cambia de color al hacer clic.

Paso 1: el HTML

Necesitas un botón con un id para que JavaScript pueda encontrarlo:

HTML 📋

<button id="miBoton">¡Haz clic aquí!</button>

🔍 ¿Por qué usamos id="miBoton"?

El id es como el "nombre" del botón. JavaScript lo va a usar para encontrarlo entre todos los elementos de la página. Es como buscar a una persona por su nombre en una multitud.

Paso 2: el JavaScript

Ahora le dices a JavaScript: cuando alguien haga clic en ese botón, ejecuta esta función.

JavaScript 📋
// Paso 1: Encontrar el botón en la página
const boton = document.getElementById("miBoton");

// Paso 2: Crear la función que va a ejecutarse
function cambiarColor() {
    boton.style.backgroundColor = "red";
    boton.textContent = "¡Funcionó!";
}

// Paso 3: Decirle al botón que escuche clics
boton.addEventListener("click", cambiarColor);

Qué hace cada línea

💡 Línea 1: const boton = document.getElementById("miBoton");

  • document = Toda tu página web
  • getElementById = "Busca el elemento que tiene este id"
  • "miBoton" = El id que pusimos en el HTML
  • const boton = = Guarda ese elemento en una variable llamada "boton"

💡 Líneas 3-6: La función cambiarColor

  • Esta función contiene las instrucciones de qué hacer cuando alguien haga clic
  • boton.style.backgroundColor = "red" → Cambiar el fondo a rojo
  • boton.textContent = "¡Funcionó!" → Cambiar el texto del botón

💡 Línea 8: addEventListener("click", cambiarColor)

  • addEventListener = "Escucha un tipo de evento"
  • "click" = El tipo de evento (en este caso, clics)
  • cambiarColor = La función que se ejecutará cuando pase el evento

❌ El error que comete todo el mundo al principio:

Escribir cambiarColor() con paréntesis en lugar de cambiarColor

Con paréntesis → la función se ejecuta al cargar la página, no al hacer clic
Sin paréntesis → la función se ejecuta cuando ocurre el evento

Comparación - Bien vs Mal 📋
// ❌ MAL: Se ejecuta al cargar la página
boton.addEventListener("click", cambiarColor());

// ✅ BIEN: Se ejecuta cuando hagan clic
boton.addEventListener("click", cambiarColor);

Pruébalo antes de seguir

✅ Verifica que tu código funcione:

1. Guarda tu archivo HTML
2. Ábrelo en tu navegador (doble clic en el archivo)
3. Haz clic en el botón
4. El botón debe volverse rojo y cambiar el texto

El event object: la información del evento

Cuando ocurre un evento, JavaScript no solo ejecuta tu función, también le pasa un objeto con información sobre lo que pasó: qué elemento se pulsó, en qué posición, qué tecla fue... La mayoría ignora esto al principio y luego se pregunta cómo detectar qué botón se pulsó cuando hay varios.

Para acceder a esa información, declara un parámetro en tu función (normalmente se llama event o simplemente e):

JavaScript con Event Object 📋
function manejarClick(event) {
    // "event" contiene información sobre el clic
    console.log("Tipo de evento:", event.type);
    console.log("Elemento clickeado:", event.target);
    console.log("Posición X del clic:", event.clientX);
    console.log("Posición Y del clic:", event.clientY);
    
    // Cambiamos el color basado en la posición X
    if (event.clientX > 200) {
        event.target.style.backgroundColor = "blue";
    } else {
        event.target.style.backgroundColor = "green";
    }
}

boton.addEventListener("click", manejarClick);

🔍 ¿Qué información me da el event object?

  • event.type → Tipo de evento ("click", "keydown", etc.)
  • event.target → El elemento que fue clickeado
  • event.clientX → Posición horizontal del clic
  • event.clientY → Posición vertical del clic
  • event.key → Qué tecla fue presionada (en eventos de teclado)

🚨 Para ver el console.log:

  1. Abre tu página en el navegador
  2. Presiona F12 (o clic derecho → "Inspeccionar")
  3. Ve a la pestaña "Console"
  4. Haz clic en tu botón
  5. ¡Verás toda la información del evento!

Los eventos más usados

Hay decenas de tipos de eventos, pero en el día a día vas a usar siempre los mismos cuatro o cinco. Estos son los que necesitas conocer:

Evento "click"

Evento Click 📋
// Se ejecuta cuando haces clic
miElemento.addEventListener("click", function() {
    alert("¡Me clickearon!");
});

Eventos "mouseover" y "mouseout"

Eventos Mouse - Hover Effects 📋
// Cuando el mouse entra al elemento
miElemento.addEventListener("mouseover", function() {
    this.style.backgroundColor = "yellow";
});

// Cuando el mouse sale del elemento
miElemento.addEventListener("mouseout", function() {
    this.style.backgroundColor = "white";
});

💡 ¿Qué es "this"?

this se refiere al elemento que disparó el evento. Es como decir "yo mismo".

Si el botón dice this.style.color = "red", está diciendo "cambia MI color a rojo".

Eventos de teclado: "keydown" y "keyup"

Eventos de Teclado 📋
// Necesitas un input para capturar teclas
const input = document.getElementById("miInput");

// Cuando presionas una tecla
input.addEventListener("keydown", function(event) {
    console.log("Presionaste:", event.key);
    
    // Ejemplo: Si presionas Enter
    if (event.key === "Enter") {
        alert("¡Presionaste Enter!");
    }
});

Evento "submit" en formularios

Evento Submit - Formularios 📋
// Para formularios completos
const formulario = document.getElementById("miFormulario");

formulario.addEventListener("submit", function(event) {
    event.preventDefault(); // ¡Importante!
    alert("Formulario enviado sin recargar página");
});

⚠️ No te olvides de event.preventDefault()

Sin esa línea, al enviar el formulario la página se recarga y pierdes todo. preventDefault() cancela el comportamiento por defecto del navegador para que puedas controlarlo tú con JavaScript.

Proyecto: Dashboard Interactivo

Juntamos todo lo anterior en un proyecto real: un dashboard que responde a clics, teclas, formularios y eventos de teclado global. Es el tipo de cosa que construyes en cualquier aplicación web.

El HTML del Dashboard

HTML Completo del Dashboard 📋
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mi Dashboard Interactivo</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f0f0f0;
        }
        .dashboard {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 20px;
        }
        .card {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        button {
            padding: 10px 20px;
            margin: 5px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        input, select {
            width: 100%;
            padding: 8px;
            margin: 5px 0;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        .contador {
            font-size: 48px;
            text-align: center;
            margin: 20px 0;
            color: #333;
        }
        .notificacion {
            display: none;
            background: #4CAF50;
            color: white;
            padding: 10px;
            position: fixed;
            top: 20px;
            right: 20px;
            border-radius: 4px;
            z-index: 1000;
        }
    </style>
</head>
<body>
    <h1>🚀 Mi Dashboard Interactivo</h1>
    
    <!-- Notificación -->
    <div id="notificacion" class="notificacion">¡Acción completada!</div>

    <div class="dashboard">
        <!-- Card 1: Botones de tema -->
        <div class="card">
            <h3>🎨 Cambiar tema</h3>
            <button id="temaClaro" style="background: #e3f2fd;">Tema Claro</button>
            <button id="temaOscuro" style="background: #424242; color: white;">Tema Oscuro</button>
            <button id="temaColorido" style="background: linear-gradient(45deg, #ff6b6b, #4ecdc4);">Tema Colorido</button>
        </div>

        <!-- Card 2: Contador -->
        <div class="card">
            <h3>🔢 Contador Interactivo</h3>
            <div id="contador" class="contador">0</div>
            <button id="sumar" style="background: #4CAF50; color: white;">+ Sumar</button>
            <button id="restar" style="background: #f44336; color: white;">- Restar</button>
            <button id="resetear" style="background: #ff9800; color: white;">Reset</button>
        </div>

        <!-- Card 3: Formulario interactivo -->
        <div class="card">
            <h3>📝 Formulario con Validación</h3>
            <form id="miFormulario">
                <label>Tu nombre:</label>
                <input type="text" id="nombre" placeholder="Escribe aquí...">
                <p id="feedback" style="color: #666; font-size: 14px;">Escribe algo...</p>
                <button type="submit" style="background: #2196F3; color: white;">Enviar</button>
            </form>
        </div>

        <!-- Card 4: Selector de opciones -->
        <div class="card">
            <h3>⚙️ Configuraciones</h3>
            <label>Modo de visualización:</label>
            <select id="modoVista">
                <option value="normal">Normal</option>
                <option value="compacto">Compacto</option>
                <option value="amplio">Amplio</option>
            </select>
            <p id="estadoVista">Modo actual: Normal</p>
        </div>
    </div>

    <script src="dashboard.js"></script>
</body>
</html>

El JavaScript del Dashboard

Ahora creamos el archivo dashboard.js con TODOS los event listeners. Te explico cada sección:

JavaScript Completo del Dashboard 📋
// 🚀 DASHBOARD INTERACTIVO - JavaScript completo
// Cada sección maneja un tipo de evento diferente

// ===== VARIABLES GLOBALES =====
let contadorActual = 0; // Para el contador

// ===== FUNCIÓN PARA MOSTRAR NOTIFICACIONES =====
function mostrarNotificacion(mensaje) {
    const notif = document.getElementById('notificacion');
    notif.textContent = mensaje;
    notif.style.display = 'block';
    
    // Ocultar después de 3 segundos
    setTimeout(function() {
        notif.style.display = 'none';
    }, 3000);
}

// ===== 1. EVENTOS CLICK - Botones de tema =====
document.getElementById('temaClaro').addEventListener('click', function() {
    document.body.style.backgroundColor = '#f0f8ff';
    document.body.style.color = '#333';
    mostrarNotificacion('✅ Tema claro activado');
});

document.getElementById('temaOscuro').addEventListener('click', function() {
    document.body.style.backgroundColor = '#2c2c2c';
    document.body.style.color = '#ffffff';
    mostrarNotificacion('🌙 Tema oscuro activado');
});

document.getElementById('temaColorido').addEventListener('click', function() {
    document.body.style.background = 'linear-gradient(45deg, #ff9a9e, #fecfef, #fecfef, #fecfef)';
    document.body.style.color = '#333';
    mostrarNotificacion('🌈 Tema colorido activado');
});

// ===== 2. EVENTOS CLICK - Contador =====
document.getElementById('sumar').addEventListener('click', function() {
    contadorActual++; // Incrementar en 1
    document.getElementById('contador').textContent = contadorActual;
    
    // Cambiar color basado en el número
    const elemento = document.getElementById('contador');
    if (contadorActual > 10) {
        elemento.style.color = 'red';
    } else if (contadorActual > 5) {
        elemento.style.color = 'orange';
    } else {
        elemento.style.color = 'green';
    }
    
    mostrarNotificacion(`➕ Contador: ${contadorActual}`);
});

document.getElementById('restar').addEventListener('click', function() {
    contadorActual--;
    document.getElementById('contador').textContent = contadorActual;
    mostrarNotificacion(`➖ Contador: ${contadorActual}`);
});

document.getElementById('resetear').addEventListener('click', function() {
    contadorActual = 0;
    document.getElementById('contador').textContent = contadorActual;
    document.getElementById('contador').style.color = '#333';
    mostrarNotificacion('🔄 Contador reseteado');
});

// ===== 3. EVENTO INPUT - Validación en tiempo real =====
document.getElementById('nombre').addEventListener('input', function(event) {
    const valor = event.target.value;
    const feedback = document.getElementById('feedback');
    
    if (valor.length === 0) {
        feedback.textContent = 'Escribe algo...';
        feedback.style.color = '#666';
    } else if (valor.length < 3) {
        feedback.textContent = '⚠️ Mínimo 3 caracteres';
        feedback.style.color = 'orange';
    } else {
        feedback.textContent = `✅ Perfecto! ${valor.length} caracteres`;
        feedback.style.color = 'green';
    }
});

// ===== 4. EVENTO SUBMIT - Formulario =====
document.getElementById('miFormulario').addEventListener('submit', function(event) {
    event.preventDefault(); // Evita que la página recargue
    
    const nombre = document.getElementById('nombre').value;
    
    if (nombre.length >= 3) {
        mostrarNotificacion(`🎉 ¡Hola ${nombre}! Formulario enviado correctamente`);
        document.getElementById('nombre').value = ''; // Limpiar
    } else {
        mostrarNotificacion('❌ El nombre debe tener al menos 3 caracteres');
    }
});

// ===== 5. EVENTO CHANGE - Select dropdown =====
document.getElementById('modoVista').addEventListener('change', function(event) {
    const modo = event.target.value;
    const dashboard = document.querySelector('.dashboard');
    const estado = document.getElementById('estadoVista');
    
    switch (modo) {
        case 'compacto':
            dashboard.style.gap = '10px';
            estado.textContent = '📦 Modo actual: Compacto';
            mostrarNotificacion('📦 Vista compacta activada');
            break;
        case 'amplio':
            dashboard.style.gap = '40px';
            estado.textContent = '📏 Modo actual: Amplio';
            mostrarNotificacion('📏 Vista amplia activada');
            break;
        default:
            dashboard.style.gap = '20px';
            estado.textContent = '⚖️ Modo actual: Normal';
            mostrarNotificacion('⚖️ Vista normal activada');
    }
});

// ===== 6. EVENTO KEYDOWN - Atajos de teclado =====
document.addEventListener('keydown', function(event) {
    // Atajos con teclas especiales
    if (event.key === 'Escape') {
        mostrarNotificacion('⚡ Presionaste Escape');
    }
    
    // Atajo: Ctrl + R para resetear contador
    if (event.ctrlKey && event.key === 'r') {
        event.preventDefault();
        contadorActual = 0;
        document.getElementById('contador').textContent = contadorActual;
        mostrarNotificacion('⚡ Contador reseteado con Ctrl+R');
    }
});

// ===== MENSAJE DE BIENVENIDA =====
document.addEventListener('DOMContentLoaded', function() {
    mostrarNotificacion('🚀 ¡Dashboard cargado! Prueba todos los elementos');
});

🧪 Prueba tu Dashboard

✅ Lista de verificación completa:

1. Guarda ambos archivos (HTML y JS)
2. Abre el HTML en tu navegador
3. Haz clic en los botones de tema - debe cambiar el fondo
4. Usa el contador - debe sumar, restar y cambiar colores
5. Escribe en el formulario - debe validar en tiempo real
6. Cambia el selector - debe modificar el espaciado
7. Presiona Escape o Ctrl+R - debe mostrar notificaciones

🚨 Si algo no funciona:

  1. Abre las herramientas de desarrollador (F12)
  2. Ve a la pestaña "Console"
  3. Busca mensajes de error en rojo
  4. Revisa que los nombres de IDs coincidan entre HTML y JavaScript
  5. Verifica que ambos archivos estén en la misma carpeta

Por dónde seguir

Con addEventListener tienes la base de cualquier interactividad en JavaScript. El siguiente escollo natural es gestionar varios elementos a la vez sin repetir listeners — para eso existe la delegación de eventos, que se explica en detalle en el post de manipulación del DOM.

Si el dashboard te ha resultado corto, prueba a añadirle persistencia: guarda el contador en localStorage para que no se reinicie al recargar. Eso solo son dos líneas más, y te obliga a entender cómo funciona realmente el ciclo de vida de la página.

🎯 Siguiente lectura recomendada

Si quieres entender bien cómo JavaScript encuentra y modifica elementos de la página antes de añadirles eventos:

👉 JavaScript DOM: cómo cambiar el contenido de una página web

La base que necesitas para trabajar con listeners en proyectos reales