HDP115

DOM y Eventos

Aprende a interactuar con páginas web mediante JavaScript, manipulando el DOM y respondiendo a eventos del usuario.

CE

Cristian Escalante

Última actualización: 21 de abril de 2025

javascript
programación web
desarrollo frontend

¿Qué es el DOM?

El DOM (Document Object Model) es una representación estructurada del documento HTML como un árbol de objetos que puede ser manipulado con JavaScript. Cada elemento HTML se convierte en un nodo del árbol, permitiendo acceder y modificar su contenido, estructura y estilo.

Estructura del DOM

Document
└── html
    ├── head
    │   ├── title
    │   ├── meta
    │   └── link
    └── body
        ├── header
        │   └── nav
        ├── main
        │   ├── section
        │   │   └── article
        │   └── aside
        └── footer

Tipos de nodos

Los principales tipos de nodos en el DOM son:

  1. Document: El nodo raíz que representa el documento HTML completo
  2. Element: Nodos que representan elementos HTML (div, p, h1, etc.)
  3. Text: Nodos que contienen texto dentro de los elementos
  4. Attribute: Nodos que representan atributos de los elementos
  5. Comment: Nodos que representan comentarios HTML

Selección de elementos

JavaScript proporciona varios métodos para seleccionar elementos del DOM:

Por ID

// Selecciona un elemento con id="miElemento"
const elemento = document.getElementById('miElemento');

Por clase

// Selecciona todos los elementos con class="miClase"
const elementos = document.getElementsByClassName('miClase');
// Devuelve una HTMLCollection (similar a un array)

Por etiqueta

// Selecciona todos los elementos <p>
const parrafos = document.getElementsByTagName('p');

Por selectores CSS (métodos modernos)

// Selecciona el primer elemento que coincida con el selector
const elemento = document.querySelector('.miClase');

// Selecciona todos los elementos que coincidan con el selector
const elementos = document.querySelectorAll('p.destacado');
// Devuelve una NodeList

Diferencias entre NodeList y HTMLCollection

  • HTMLCollection: Colección "viva" que se actualiza automáticamente cuando el DOM cambia
  • NodeList: Colección estática (excepto algunas como childNodes)
  • Conversión a Array: Para usar métodos de array como map o filter
// Convertir a array
const elementosArray = Array.from(document.querySelectorAll('.miClase'));
// o
const elementosArray = [...document.querySelectorAll('.miClase')];

Modificación del contenido y atributos

Contenido de texto

// Obtener o establecer el contenido de texto
const titulo = document.querySelector('h1');
console.log(titulo.textContent); // Obtener texto

titulo.textContent = 'Nuevo título'; // Establecer texto

Contenido HTML

// Obtener o establecer el HTML interno
const contenedor = document.querySelector('.contenedor');
console.log(contenedor.innerHTML); // Obtener HTML

contenedor.innerHTML = '<p>Nuevo <strong>contenido</strong></p>'; // Establecer HTML

Nota de seguridad: Ten cuidado al usar innerHTML con contenido generado por usuarios, ya que puede crear vulnerabilidades XSS (Cross-Site Scripting).

Manipulación de atributos

const enlace = document.querySelector('a');

// Obtener atributos
console.log(enlace.getAttribute('href'));
console.log(enlace.href); // Acceso directo para atributos comunes

// Establecer atributos
enlace.setAttribute('href', 'https://ejemplo.com');
enlace.href = 'https://ejemplo.com'; // Acceso directo

// Comprobar si existe un atributo
console.log(enlace.hasAttribute('target'));

// Eliminar atributos
enlace.removeAttribute('target');

Manipulación de clases CSS

const elemento = document.querySelector('.miElemento');

// Añadir clases
elemento.classList.add('destacado');

// Eliminar clases
elemento.classList.remove('oculto');

// Alternar clases (añadir si no existe, eliminar si existe)
elemento.classList.toggle('activo');

// Comprobar si tiene una clase
if (elemento.classList.contains('destacado')) {
  console.log('El elemento tiene la clase destacado');
}

// Reemplazar una clase
elemento.classList.replace('vieja', 'nueva');

Manipulación de estilos

const elemento = document.querySelector('.miElemento');

// Establecer estilos directamente
elemento.style.color = 'red';
elemento.style.backgroundColor = '#f0f0f0';
elemento.style.fontSize = '16px';

// Obtener estilos computados (después de aplicar CSS)
const estilos = window.getComputedStyle(elemento);
console.log(estilos.color);
console.log(estilos.fontSize);

Creación y eliminación de elementos

Crear elementos

// Crear un nuevo elemento
const nuevoParrafo = document.createElement('p');
nuevoParrafo.textContent = 'Este es un nuevo párrafo';
nuevoParrafo.classList.add('destacado');

// Crear un nodo de texto
const textoNodo = document.createTextNode('Texto simple');

Añadir elementos al DOM

const contenedor = document.querySelector('.contenedor');
const nuevoParrafo = document.createElement('p');
nuevoParrafo.textContent = 'Nuevo párrafo';

// Añadir al final del contenedor
contenedor.appendChild(nuevoParrafo);

// Añadir en una posición específica
const primerHijo = contenedor.firstChild;
contenedor.insertBefore(nuevoParrafo, primerHijo);

// Métodos modernos
contenedor.append(nuevoParrafo); // Al final (permite múltiples nodos y texto)
contenedor.prepend(nuevoParrafo); // Al principio
contenedor.before(nuevoParrafo); // Antes del elemento
contenedor.after(nuevoParrafo); // Después del elemento

Clonar elementos

const original = document.querySelector('.original');

// Clonar sin los descendientes
const clon = original.cloneNode(false);

// Clonar con todos los descendientes
const clonCompleto = original.cloneNode(true);

Eliminar elementos

const elemento = document.querySelector('.eliminar');

// Método antiguo
elemento.parentNode.removeChild(elemento);

// Método moderno
elemento.remove();

Reemplazar elementos

const viejo = document.querySelector('.viejo');
const nuevo = document.createElement('div');
nuevo.textContent = 'Elemento nuevo';

// Método antiguo
viejo.parentNode.replaceChild(nuevo, viejo);

// Método moderno
viejo.replaceWith(nuevo);

Relaciones padre-hijo

const padre = document.querySelector('.padre');

// Hijos
const hijos = padre.children; // HTMLCollection de elementos hijos
const primerHijo = padre.firstElementChild;
const ultimoHijo = padre.lastElementChild;
const todosLosNodos = padre.childNodes; // NodeList (incluye nodos de texto)

// Padre
const elementoPadre = hijo.parentElement;
const nodoPadre = hijo.parentNode;

Relaciones entre hermanos

const elemento = document.querySelector('.medio');

// Hermanos
const siguiente = elemento.nextElementSibling;
const anterior = elemento.previousElementSibling;

// Incluye nodos de texto
const siguienteNodo = elemento.nextSibling;
const anteriorNodo = elemento.previousSibling;

Recorrer el DOM

function recorrerDOM(elemento, callback) {
  callback(elemento);
  
  // Recorrer todos los hijos
  const hijos = elemento.children;
  for (let i = 0; i < hijos.length; i++) {
    recorrerDOM(hijos[i], callback);
  }
}

// Ejemplo de uso
recorrerDOM(document.body, (elemento) => {
  console.log(elemento.tagName);
});

Introducción a los eventos

Los eventos son acciones o sucesos que ocurren en el navegador, como clics, pulsaciones de teclas o carga de la página.

Tipos comunes de eventos

  • Mouse: click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout
  • Teclado: keydown, keyup, keypress
  • Formulario: submit, reset, change, input, focus, blur
  • Documento: DOMContentLoaded, load, unload, resize, scroll
  • Arrastrar: dragstart, drag, dragend, drop
  • Táctil: touchstart, touchmove, touchend

Asignar manejadores de eventos

Método addEventListener

const boton = document.querySelector('#miBoton');

boton.addEventListener('click', function(evento) {
  console.log('Botón clickeado');
  console.log(evento); // Objeto del evento
});

// Con función flecha
boton.addEventListener('click', (e) => {
  console.log(`Clickeado en: ${e.clientX}, ${e.clientY}`);
});

// Con función nombrada
function manejadorClick(e) {
  console.log('Click detectado');
}

boton.addEventListener('click', manejadorClick);

// Eliminar un manejador de eventos
boton.removeEventListener('click', manejadorClick);

Propiedades de eventos

// Método antiguo (no recomendado para código moderno)
boton.onclick = function() {
  console.log('Botón clickeado');
};

// Sobrescribe cualquier manejador anterior
boton.onclick = otraFuncion; // El manejador anterior se pierde

Eventos en línea (no recomendado)

<button onclick="console.log('Clickeado')">Haz clic</button>

El objeto Event

Cada manejador de eventos recibe un objeto Event con información sobre el evento:

boton.addEventListener('click', function(evento) {
  // Información general
  console.log(evento.type); // "click"
  console.log(evento.target); // Elemento que disparó el evento
  console.log(evento.currentTarget); // Elemento al que está adjunto el manejador
  
  // Coordenadas (para eventos de mouse)
  console.log(evento.clientX, evento.clientY); // Relativas a la ventana
  console.log(evento.pageX, evento.pageY); // Relativas al documento
  
  // Teclas (para eventos de teclado)
  console.log(evento.key); // Tecla presionada
  console.log(evento.keyCode); // Código de la tecla (obsoleto)
  console.log(evento.ctrlKey, evento.shiftKey); // Teclas modificadoras
  
  // Prevenir comportamiento predeterminado
  evento.preventDefault();
  
  // Detener propagación
  evento.stopPropagation();
});

Event bubbling y capturing

Cuando ocurre un evento en un elemento, este se propaga a través del DOM:

Fases de propagación

  1. Fase de captura: El evento desciende desde el documento hasta el elemento objetivo
  2. Fase de objetivo: El evento llega al elemento donde ocurrió
  3. Fase de burbujeo: El evento asciende desde el elemento objetivo hasta el documento
// El tercer parámetro controla la fase:
// false (predeterminado): fase de burbujeo
// true: fase de captura
elemento.addEventListener('click', manejador, false); // Burbujeo
elemento.addEventListener('click', manejador, true); // Captura

// Ejemplo que muestra la propagación
document.body.addEventListener('click', () => {
  console.log('Click en body (burbujeo)');
});

document.querySelector('div').addEventListener('click', (e) => {
  console.log('Click en div (burbujeo)');
  // Detener la propagación
  // e.stopPropagation();
});

document.querySelector('button').addEventListener('click', () => {
  console.log('Click en botón (burbujeo)');
});

Detener la propagación

elemento.addEventListener('click', (e) => {
  e.stopPropagation(); // Detiene la propagación
  e.stopImmediatePropagation(); // Detiene la propagación y otros manejadores en el mismo elemento
});

Delegación de eventos

La delegación de eventos es una técnica que aprovecha el burbujeo para manejar eventos en múltiples elementos con un solo manejador:

// En lugar de asignar un manejador a cada botón
document.querySelector('.contenedor').addEventListener('click', (e) => {
  // Comprobar si el clic fue en un botón
  if (e.target.matches('button') || e.target.closest('button')) {
    console.log('Botón clickeado:', e.target);
    
    // Acceder a atributos de datos
    const id = e.target.dataset.id;
    if (id) {
      console.log(`ID del botón: ${id}`);
    }
  }
});

Ventajas de la delegación de eventos

  1. Rendimiento mejorado: Menos manejadores de eventos
  2. Manejo dinámico: Funciona con elementos añadidos después de cargar la página
  3. Menos código: Simplifica la gestión de eventos en listas o tablas grandes

Ejemplo práctico: Lista de tareas

const lista = document.querySelector('#tareas');

lista.addEventListener('click', (e) => {
  const tarea = e.target.closest('.tarea');
  if (!tarea) return; // No se hizo clic en una tarea
  
  if (e.target.matches('.eliminar')) {
    // Clic en el botón eliminar
    tarea.remove();
  } else if (e.target.matches('.completar')) {
    // Clic en el botón completar
    tarea.classList.toggle('completada');
  } else {
    // Clic en la tarea
    console.log('Tarea seleccionada:', tarea.textContent);
  }
});

Eventos personalizados

JavaScript permite crear y disparar eventos personalizados:

// Crear un evento personalizado
const eventoPersonalizado = new CustomEvent('miEvento', {
  detail: { 
    mensaje: 'Hola desde evento personalizado',
    timestamp: Date.now()
  },
  bubbles: true,
  cancelable: true
});

// Escuchar el evento personalizado
document.addEventListener('miEvento', (e) => {
  console.log('Evento personalizado recibido:', e.detail.mensaje);
});

// Disparar el evento
document.dispatchEvent(eventoPersonalizado);

Mejores prácticas

  1. Usar addEventListener en lugar de propiedades de eventos:
    // Bien
    elemento.addEventListener('click', manejador);
    
    // Evitar
    elemento.onclick = manejador;
    
  2. Delegar eventos cuando sea posible:
    // Bien
    contenedor.addEventListener('click', manejarClicsEnHijos);
    
    // Evitar (para muchos elementos)
    elementos.forEach(el => el.addEventListener('click', manejador));
    
  3. Eliminar manejadores cuando ya no sean necesarios:
    // Guardar referencia a la función
    const manejador = (e) => { /* código */ };
    elemento.addEventListener('click', manejador);
    
    // Eliminar cuando no sea necesario
    elemento.removeEventListener('click', manejador);
    
  4. Usar funciones nombradas para manejadores complejos:
    // Bien
    function manejarEnvio(e) {
      // Lógica compleja
    }
    formulario.addEventListener('submit', manejarEnvio);
    
    // Evitar para lógica compleja
    formulario.addEventListener('submit', function(e) {
      // Mucho código...
    });
    
  5. Prevenir comportamientos predeterminados con cuidado:
    formulario.addEventListener('submit', (e) => {
      // Prevenir solo cuando sea necesario
      if (!validarFormulario()) {
        e.preventDefault();
      }
    });
    
Arrays y Métodos
Aprende a trabajar con arrays en JavaScript, desde operacion...
Asincronía, Callbacks y Promesas
Aprende a manejar operaciones asíncronas en JavaScript media...

Conceptos Básicos de HTML

Aprende los conceptos básicos de HTML

Conceptos Básicos de CSS

Aprende los conceptos básicos de CSS

Conceptos Básicos SQL

Aprende los conceptos básicos de SQL

Conceptos Básicos de GIT

Aprende los conceptos básicos de GIT

Conceptos Básicos de Python

Aprende los conceptos básicos de Python

Conceptos Básicos de UML

Aprende los conceptos básicos de UML

Refuerzo Academico de Herramientas de Productividad 2025