Ir al contenido principal

Creación y eliminación de elementos

Introducción

Hasta ahora hemos aprendido a seleccionar elementos del DOM y modificar su contenido y atributos. El siguiente paso es dominar la creación y eliminación de elementos, lo que nos permitirá alterar dinámicamente la estructura de nuestra página web. Esta capacidad es fundamental para crear interfaces interactivas que respondan a las acciones del usuario o a datos recibidos del servidor. En este artículo, exploraremos cómo crear nuevos elementos HTML, insertarlos en el documento, clonar elementos existentes y eliminar aquellos que ya no necesitamos.

Creación de elementos con createElement

El método createElement nos permite crear nuevos elementos HTML desde JavaScript:

// Crear un nuevo elemento párrafo
const nuevoPárrafo = document.createElement("p");

// Crear un nuevo elemento imagen
const nuevaImagen = document.createElement("img");

// Crear un nuevo elemento botón
const nuevoBoton = document.createElement("button");

El método acepta como parámetro el nombre de la etiqueta HTML que queremos crear. Sin embargo, estos elementos recién creados aún no forman parte del documento. Son como elementos "flotantes" que necesitan ser insertados para que el usuario los vea.

Configuración del nuevo elemento

Después de crear un elemento, necesitamos configurarlo antes de insertarlo en el DOM:

// Crear un nuevo elemento párrafo
const parrafo = document.createElement("p");

// Añadir contenido de texto
parrafo.textContent = "Este es un nuevo párrafo creado con JavaScript";

// Añadir atributos
parrafo.className = "destacado";
parrafo.id = "parrafo-nuevo";
parrafo.setAttribute("data-info", "creado-dinamicamente");

// Añadir estilos inline (si es necesario)
parrafo.style.color = "blue";
parrafo.style.marginTop = "20px";

También podemos crear y configurar elementos más complejos, como imágenes o enlaces:

// Crear una imagen
const imagen = document.createElement("img");
imagen.src = "imagen.jpg";
imagen.alt = "Descripción de la imagen";
imagen.width = 300;

// Crear un enlace
const enlace = document.createElement("a");
enlace.href = "https://ejemplo.com";
enlace.textContent = "Visitar sitio web";
enlace.target = "_blank";

Inserción de elementos en el DOM

Una vez que hemos creado y configurado un elemento, tenemos varias formas de insertarlo en el DOM.

Métodos append y prepend

Los métodos modernos append y prepend nos permiten añadir elementos al principio o al final de un elemento contenedor:

const contenedor = document.getElementById("contenedor");
const nuevoElemento = document.createElement("div");
nuevoElemento.textContent = "Nuevo contenido";

// Añadir al final del contenedor
contenedor.append(nuevoElemento);

// Añadir al principio del contenedor
// contenedor.prepend(nuevoElemento);

// Podemos añadir múltiples elementos a la vez
// contenedor.append(elemento1, elemento2, "También podemos añadir texto directamente");

Métodos appendChild y insertBefore

Estos métodos más tradicionales también permiten insertar elementos:

const lista = document.getElementById("mi-lista");
const nuevoItem = document.createElement("li");
nuevoItem.textContent = "Nuevo elemento de lista";

// Añadir al final con appendChild
lista.appendChild(nuevoItem);

// Insertar antes de un elemento específico
const referenciaItem = document.getElementById("item-referencia");
const otroItem = document.createElement("li");
otroItem.textContent = "Elemento insertado en medio";

lista.insertBefore(otroItem, referenciaItem);

Diferencias clave:

  • appendChild solo acepta nodos (no texto directo) y solo uno a la vez
  • append puede recibir múltiples nodos y también cadenas de texto
  • insertBefore requiere un nodo de referencia

Métodos after y before

Los métodos after y before permiten insertar elementos adyacentes a un elemento existente:

const elementoReferencia = document.getElementById("elemento-referencia");
const nuevoElemento = document.createElement("p");
nuevoElemento.textContent = "Elemento adyacente";

// Insertar después del elemento de referencia
elementoReferencia.after(nuevoElemento);

// Insertar antes del elemento de referencia
// elementoReferencia.before(nuevoElemento);

Método insertAdjacentElement

Este método ofrece un control más preciso sobre la ubicación de inserción:

const referencia = document.getElementById("referencia");
const nuevoElemento = document.createElement("div");
nuevoElemento.textContent = "Elemento posicionado con precisión";

// Posiciones disponibles:
referencia.insertAdjacentElement("beforebegin", nuevoElemento); // Antes del elemento
// referencia.insertAdjacentElement("afterbegin", nuevoElemento); // Dentro, al principio
// referencia.insertAdjacentElement("beforeend", nuevoElemento); // Dentro, al final
// referencia.insertAdjacentElement("afterend", nuevoElemento); // Después del elemento

Clonar elementos existentes

A veces es más conveniente clonar un elemento existente que crear uno nuevo desde cero:

const elementoOriginal = document.getElementById("template-item");
const clon = elementoOriginal.cloneNode(false);

// cloneNode(false) clona solo el elemento
// cloneNode(true) clona el elemento y todos sus descendientes

// Modificar el clon
clon.id = "nuevo-id";
clon.textContent = "Contenido del clon";

// Insertar el clon en el documento
document.getElementById("contenedor").appendChild(clon);

La clonación es particularmente útil cuando necesitamos crear múltiples elementos con estructura similar, como filas de una tabla o elementos de una lista.

Eliminar elementos con remove

Para eliminar elementos del DOM, el método moderno más sencillo es remove():

const elementoAEliminar = document.getElementById("elemento-obsoleto");

// Eliminar el elemento directamente
elementoAEliminar.remove();

// También podemos verificar primero si existe
if (elementoAEliminar) {
    elementoAEliminar.remove();
}

Reemplazar elementos

Podemos reemplazar un elemento existente por uno nuevo:

const elementoViejo = document.getElementById("elemento-antiguo");
const elementoNuevo = document.createElement("div");
elementoNuevo.textContent = "Soy el reemplazo";
elementoNuevo.className = "destacado";

// Método moderno
elementoViejo.replaceWith(elementoNuevo);

// Método tradicional (desde el padre)
// const padre = elementoViejo.parentNode;
// padre.replaceChild(elementoNuevo, elementoViejo);

Fragmentos de documento

Los fragmentos de documento son contenedores temporales que nos permiten preparar un conjunto de elementos antes de insertarlos en el DOM, mejorando significativamente el rendimiento:

// Crear un fragmento
const fragmento = document.createDocumentFragment();

// Añadir elementos al fragmento
for (let i = 1; i <= 100; i++) {
    const item = document.createElement("li");
    item.textContent = `Elemento ${i}`;
    fragmento.appendChild(item);
}

// Insertar todo el fragmento de una sola vez
const lista = document.getElementById("mi-lista");
lista.appendChild(fragmento);

Usar fragmentos tiene ventajas importantes:

  • Evita múltiples reflow y repaint (mejora el rendimiento)
  • No crea un nodo contenedor adicional
  • Es la forma más eficiente de insertar múltiples elementos

Rendimiento en manipulaciones masivas

La manipulación del DOM puede ser costosa en términos de rendimiento, especialmente cuando añadimos o eliminamos muchos elementos. Aquí hay algunas estrategias para optimizar estas operaciones:

Problema: Añadir muchos elementos individualmente

// INEFICIENTE: Causa múltiples reflow
const lista = document.getElementById("mi-lista");
for (let i = 0; i < 1000; i++) {
    const item = document.createElement("li");
    item.textContent = `Item ${i}`;
    lista.appendChild(item); // Cada append causa un reflow
}

Solución 1: Usar un fragmento de documento

// EFICIENTE: Un solo reflow al final
const lista = document.getElementById("mi-lista");
const fragmento = document.createDocumentFragment();

for (let i = 0; i < 1000; i++) {
    const item = document.createElement("li");
    item.textContent = `Item ${i}`;
    fragmento.appendChild(item);
}

lista.appendChild(fragmento); // Un solo reflow

Solución 2: Construcción con innerHTML (cuidado con la seguridad)

// EFICIENTE pero potencialmente inseguro con datos no controlados
const lista = document.getElementById("mi-lista");
let html = "";

for (let i = 0; i < 1000; i++) {
    html += `<li>Item ${i}</li>`;
}

lista.innerHTML = html; // Un solo reflow

Solución 3: Desconectar y reconectar el nodo

// EFICIENTE: Trabajar fuera del DOM visible
const lista = document.getElementById("mi-lista");
const padre = lista.parentNode;
const siguiente = lista.nextSibling;

// Desconectar del DOM
padre.removeChild(lista);

// Realizar múltiples operaciones
for (let i = 0; i < 1000; i++) {
    const item = document.createElement("li");
    item.textContent = `Item ${i}`;
    lista.appendChild(item);
}

// Reconectar al DOM
padre.insertBefore(lista, siguiente);

Ejemplos prácticos

Ejemplo 1: Lista de tareas dinámica

// HTML básico:
// <input id="nueva-tarea" type="text" placeholder="Nueva tarea...">
// <button id="agregar">Agregar</button>
// <ul id="lista-tareas"></ul>

document.getElementById("agregar").addEventListener("click", function() {
    const input = document.getElementById("nueva-tarea");
    const textoTarea = input.value.trim();
    
    if (textoTarea) {
        // Crear elementos
        const nuevaTarea = document.createElement("li");
        nuevaTarea.className = "tarea";
        
        const textoElemento = document.createElement("span");
        textoElemento.textContent = textoTarea;
        
        const botonEliminar = document.createElement("button");
        botonEliminar.textContent = "Eliminar";
        botonEliminar.className = "eliminar";
        
        // Añadir manejador de eventos al botón eliminar
        botonEliminar.addEventListener("click", function() {
            nuevaTarea.remove();
        });
        
        // Estructurar y añadir al DOM
        nuevaTarea.appendChild(textoElemento);
        nuevaTarea.appendChild(botonEliminar);
        
        document.getElementById("lista-tareas").appendChild(nuevaTarea);
        
        // Limpiar el input
        input.value = "";
        input.focus();
    }
});

Ejemplo 2: Tabla dinámica desde datos JSON

// Datos de ejemplo
const personas = [
    { id: 1, nombre: "Ana", edad: 28, ciudad: "Madrid" },
    { id: 2, nombre: "Carlos", edad: 34, ciudad: "Barcelona" },
    { id: 3, nombre: "Elena", edad: 23, ciudad: "Valencia" }
];

function generarTabla(datos, contenedor) {
    // Crear elementos de tabla
    const tabla = document.createElement("table");
    tabla.className = "tabla-datos";
    
    // Crear encabezado
    const thead = document.createElement("thead");
    const filaCabecera = document.createElement("tr");
    
    // Obtener las claves del primer objeto para las columnas
    const columnas = Object.keys(datos[0]);
    
    // Añadir cabeceras
    columnas.forEach(columna => {
        const th = document.createElement("th");
        th.textContent = columna.charAt(0).toUpperCase() + columna.slice(1); // Capitalizar
        filaCabecera.appendChild(th);
    });
    
    // Añadir columna adicional para acciones
    const thAcciones = document.createElement("th");
    thAcciones.textContent = "Acciones";
    filaCabecera.appendChild(thAcciones);
    
    thead.appendChild(filaCabecera);
    tabla.appendChild(thead);
    
    // Crear cuerpo de la tabla
    const tbody = document.createElement("tbody");
    
    // Añadir filas de datos
    datos.forEach(objeto => {
        const fila = document.createElement("tr");
        fila.dataset.id = objeto.id;
        
        // Añadir celdas con datos
        columnas.forEach(columna => {
            const celda = document.createElement("td");
            celda.textContent = objeto[columna];
            fila.appendChild(celda);
        });
        
        // Añadir celda con botón eliminar
        const celdaAcciones = document.createElement("td");
        const botonEliminar = document.createElement("button");
        botonEliminar.textContent = "Eliminar";
        botonEliminar.className = "btn-eliminar";
        
        botonEliminar.addEventListener("click", function() {
            fila.remove();
        });
        
        celdaAcciones.appendChild(botonEliminar);
        fila.appendChild(celdaAcciones);
        
        tbody.appendChild(fila);
    });
    
    tabla.appendChild(tbody);
    
    // Limpiar el contenedor e insertar la tabla
    const elementoContenedor = document.getElementById(contenedor);
    elementoContenedor.innerHTML = "";
    elementoContenedor.appendChild(tabla);
}

// Uso:
generarTabla(personas, "contenedor-tabla");

Ejemplo 3: Creación de una galería de imágenes

// Datos de ejemplo
const imagenes = [
    { url: "imagen1.jpg", titulo: "Paisaje de montaña" },
    { url: "imagen2.jpg", titulo: "Playa al atardecer" },
    { url: "imagen3.jpg", titulo: "Bosque en otoño" }
];

function crearGaleria(imagenes, contenedorId) {
    const contenedor = document.getElementById(contenedorId);
    contenedor.className = "galeria";
    
    // Usar un fragmento para mejorar el rendimiento
    const fragmento = document.createDocumentFragment();
    
    imagenes.forEach(img => {
        // Crear elemento contenedor para cada imagen
        const tarjeta = document.createElement("div");
        tarjeta.className = "tarjeta-imagen";
        
        // Crear la imagen
        const imagen = document.createElement("img");
        imagen.src = img.url;
        imagen.alt = img.titulo;
        
        // Crear el título
        const titulo = document.createElement("p");
        titulo.className = "titulo-imagen";
        titulo.textContent = img.titulo;
        
        // Añadir eventos
        tarjeta.addEventListener("click", function() {
            mostrarImagenAmpliada(img.url, img.titulo);
        });
        
        // Estructurar la tarjeta
        tarjeta.appendChild(imagen);
        tarjeta.appendChild(titulo);
        
        // Añadir al fragmento
        fragmento.appendChild(tarjeta);
    });
    
    // Insertar todo en el contenedor
    contenedor.appendChild(fragmento);
}

function mostrarImagenAmpliada(url, titulo) {
    // Crear elementos para el modal
    const modal = document.createElement("div");
    modal.className = "modal";
    
    const modalContenido = document.createElement("div");
    modalContenido.className = "modal-contenido";
    
    const imagen = document.createElement("img");
    imagen.src = url;
    imagen.alt = titulo;
    
    const textoTitulo = document.createElement("h3");
    textoTitulo.textContent = titulo;
    
    const botonCerrar = document.createElement("button");
    botonCerrar.textContent = "×";
    botonCerrar.className = "cerrar-modal";
    
    // Añadir evento para cerrar
    botonCerrar.addEventListener("click", function() {
        modal.remove();
    });
    
    // También cerrar al hacer clic fuera del contenido
    modal.addEventListener("click", function(e) {
        if (e.target === modal) {
            modal.remove();
        }
    });
    
    // Estructurar el modal
    modalContenido.appendChild(botonCerrar);
    modalContenido.appendChild(imagen);
    modalContenido.appendChild(textoTitulo);
    modal.appendChild(modalContenido);
    
    // Añadir al body
    document.body.appendChild(modal);
}

// Uso:
crearGaleria(imagenes, "contenedor-galeria");

Resumen

En este artículo hemos explorado las técnicas para crear, insertar, clonar, reemplazar y eliminar elementos del DOM. Hemos aprendido a utilizar métodos como createElement, append, appendChild, insertBefore, remove y replaceWith. También hemos visto cómo utilizar fragmentos de documento para mejorar el rendimiento en manipulaciones masivas.

Estas técnicas son fundamentales para crear interfaces dinámicas que respondan a las interacciones del usuario o a datos recibidos del servidor. En el próximo artículo, aprenderemos sobre los diferentes tipos de eventos en JavaScript, lo que nos permitirá responder a las acciones del usuario y crear experiencias interactivas más completas.