libro / blog / javascript-dom-fetch

// capítulo 04 · javascript

JavaScript y el DOM: eventos, fetch y AJAX sin librerías

Gustavo Arias9 min de lectura

TL;DR Selecciona con querySelector, modifica con textContent y classList, escucha con addEventListener y habla con el servidor con fetch + async/await. Hoy todo eso es JavaScript nativo: no necesitas jQuery.

JavaScript es la capa que convierte una página estática en una aplicación. En una arquitectura full stack es además el puente entre lo que ve el usuario y tu backend. En el libro Programación Full-Stack lo usamos para validar formularios, actualizar listas sin recargar y consumir las APIs que construimos con PHP. Aquí tienes lo esencial.

Qué es el DOM

El DOM (Document Object Model) es la representación en memoria de tu HTML: un árbol de objetos que JavaScript puede leer y modificar. Cuando cambias un nodo del DOM, el navegador vuelve a pintar esa parte de la página al instante.

Seleccionar y modificar elementos

// Seleccionar: usa la misma sintaxis que en CSS
const titulo = document.querySelector('h1');
const botones = document.querySelectorAll('.btn'); // NodeList

// Modificar texto (seguro: no interpreta HTML)
titulo.textContent = 'Hola, full stack';

// Clases: mejor que tocar estilos uno a uno
titulo.classList.add('destacado');
titulo.classList.toggle('oculto');

// Atributos
const enlace = document.querySelector('a');
enlace.setAttribute('href', '/contacto');

// Crear e insertar elementos
const item = document.createElement('li');
item.textContent = 'Nuevo elemento';
document.querySelector('#lista').append(item);
Evita innerHTML con datos que vengan del usuario o del servidor: si contienen <script> o atributos como onerror, abres la puerta a ataques XSS. Usa textContent o crea los nodos con createElement.

Eventos: reaccionar a lo que hace el usuario

Un evento es algo que ocurre en la página: un clic, una tecla, el envío de un formulario. Se escucha con addEventListener.

const boton = document.querySelector('#menu-toggle');
const menu = document.querySelector('#menu');

boton.addEventListener('click', () => {
    const abierto = menu.classList.toggle('abierto');
    boton.setAttribute('aria-expanded', abierto); // accesibilidad
});

// Delegación: un solo listener para muchos elementos,
// incluso los que se agreguen después
document.querySelector('#lista').addEventListener('click', (e) => {
    const borrar = e.target.closest('.borrar');
    if (!borrar) return;
    borrar.closest('li').remove();
});

La delegación de eventos es clave cuando generas filas dinámicamente (por ejemplo, la tabla de un CRUD): en lugar de añadir un listener por fila, escuchas en el contenedor y miras e.target.

AJAX moderno con fetch y async/await

AJAX es la técnica de pedir datos al servidor sin recargar la página. Hoy se hace con fetch, que devuelve una promesa. Con async/await el código se lee de arriba abajo, como si fuera síncrono.

async function cargarProductos() {
    try {
        const res = await fetch('api/productos.php');

        // fetch NO lanza error con 404 o 500: hay que comprobarlo
        if (!res.ok) {
            throw new Error(`Error HTTP ${res.status}`);
        }

        const productos = await res.json();
        const lista = document.querySelector('#productos');
        lista.replaceChildren(); // vaciar

        for (const p of productos) {
            const li = document.createElement('li');
            li.textContent = `${p.nombre} – $${p.precio}`;
            lista.append(li);
        }
    } catch (error) {
        console.error(error);
        document.querySelector('#estado').textContent = 'No se pudieron cargar los productos.';
    }
}

cargarProductos();

Enviar un formulario sin recargar la página

Este patrón aparece en casi cualquier aplicación: interceptas el submit, envías los datos con fetch y muestras el resultado.

const form = document.querySelector('#form-contacto');

form.addEventListener('submit', async (e) => {
    e.preventDefault(); // evita la recarga

    const boton = form.querySelector('button');
    boton.disabled = true; // evita envíos dobles

    try {
        const res = await fetch('contacto.php', {
            method: 'POST',
            body: new FormData(form) // llega a PHP en $_POST
        });
        const data = await res.json();
        alert(data.mensaje);
        form.reset();
    } catch {
        alert('Error de conexión. Intenta de nuevo.');
    } finally {
        boton.disabled = false;
    }
});

Usar FormData tiene una ventaja enorme: en el servidor, PHP recibe los campos en $_POST como si fuera un formulario normal. En el artículo sobre formularios en PHP vemos cómo procesarlos y validarlos del lado del servidor.

Errores comunes

Referencia recomendada: la documentación de Fetch API en MDN.

Conecta tu frontend con un backend real

En el libro Programación Full-Stack unimos este JavaScript con PHP y MySQL para construir un blog, un sistema de usuarios y una tienda online completos.

Comprar en Amazon