UT03: JavaScript Moderno

Desarrollo de Interfaces Web - Implantación de Aplicaciones Web

2º DAM y ASIR

Introducción Usando JavaScript Buscando en el DOM Transversal Manipulando el DOM Interacciones Filtrando
Portada - Artean, bolsa de empleo de Cuatrovientos

¿Qué es JavaScript?

¿Qué es JavaScript? — JavaScript es un lenguaje de programación de alto nivel, interpretado y dinámico. Es el lenguaje nativo de la web, ejecutado directamente por los navegadores sin necesidad de compilación. Permite crear contenido dinámico, controlar multimedia, animaciones y mucho más. Junto con HTML y CSS, es una de las tres tecnologías fundamentales de la World Wide Web.
Referencia oficial: MDN Web Docs - JavaScript
Logo JavaScript

¿Qué es JavaScript Moderno?

Desarrollo de Interfaces Web

¿Qué es JavaScript?

Con JavaScript es fácil…

Buscar:

elementos en un documento HTML

Cambiar:

contenido HTML

Escuchar:

las acciones de usuario y actuar en consecuencia.

Animar:

contenido dentro de la página

Cambiando el contenido

Documento HTML

<!DOCTYPE html> <html> <head> <title>Artean</title> </head> <body> <h1>Bolsa de empleo de Cuatrovientos</h1> <p>Queremos dar la bienvenida....</p> </body> </html>

¿Cómo modificar el texto de <h1>?

document.querySelector('h1') .textContent = 'Nuevo texto';

¿Cómo podemos buscar a través de nuestro HTML?

Tenemos que entender cómo organiza el HTML que recibe nuestro navegador.

<!DOCTYPE html> <html> <head> <title>Artean</title> </head> <body> <h1>Bolsa de empleo...</h1> <p>Queremos dar...</p> </body> </html>

El navegador crea el DOM

Una estructura de árbol que JavaScript puede manipular.

Document Object Model (DOM)

Es una estructura similar a un árbol, creada por los navegadores para que podamos encontrar rápidamente elementos HTML utilizando JavaScript.

"DOM" — JavaScript ofrece a los desarrolladores un lenguaje para interactuar con el DOM.

Cargando el HTML en el DOM

Navegador → DOM

DOCUMENTO └─ html ├─ head │ └─ title │ └─ "Artean" └─ body ├─ h1 │ └─ "Bolsa de empleo..." └─ p └─ "Queremos dar..."

Proceso de carga

El navegador parsea el HTML y construye el DOM como un árbol de nodos.

Nodos y Relaciones en el DOM

Dentro del DOM, los elementos HTML se convierten en "nodos" que tienen relaciones entre sí.

DOCUMENTO ├─ html │ ├─ headtitle"Artean" │ └─ body │ ├─ h1"Bolsa de empleo..." │ └─ p"Queremos dar la..."

Tipos de nodos: elemento, texto

JavaScript se ejecuta en el navegador y puede leer/escribir el DOM directamente.

Pero, ¿hay un problema?

El problema

Cada explorador tiene una interfaz del DOM con pequeñas particularidades.

Antes: Se usaban librerías para normalizar diferencias entre navegadores.

¡JavaScript Moderno al rescate!

Si usas JavaScript nativo con las APIs del DOM modernas, funcionará en todos los navegadores modernos.

Las APIs del DOM como querySelector, addEventListener, classList son estándar y universales.
jQuery RIP

¿Cómo encontramos elementos haciendo uso del DOM?

Servidor Apache / Nginx HTML + Recursos Navegador Chrome / Firefox + JavaScript DOM Árbol de nodos Manipulable con JS 1. Pide HTML 2. Envía HTML 3. Carga DOM JavaScript interactúa 4. Lee / Escribe

Flujo de comunicación

PasoDescripción
1Navegador pide HTML al Servidor
2Servidor envía el HTML
3Navegador carga el HTML en el DOM
4JavaScript interactúa con el DOM

JavaScript es nativo

No necesita librerías externas. El navegador ya trae todo lo necesario.

jQuery RIP

Uso básico de JavaScript

document.querySelector("h1") // primer elemento document.querySelectorAll("h1") // todos los elementos

Cómo accede JavaScript al DOM

// El DOM ya está disponible document // objeto global
El objeto document es la puerta de entrada al DOM en JavaScript nativo.

Selectores CSS

Hacemos uso de los selectores CSS para encontrar elementos:

h1 { font-size: 3em; } p { color: blue; }
document.querySelector("h1") // primer elemento document.querySelectorAll("p") // todos los elementos

Selectores en JavaScript Moderno

document.querySelector("h1"); // primer elemento document.querySelectorAll("p"); // todos los elementos
querySelector acepta cualquier selector CSS válido. querySelectorAll retorna todos los elementos.

Si queremos cambiar el contenido…

Primero buscamos, luego cambiamos.

Documento HTML

<h1>Bolsa de empleo de Cuatrovientos</h1> <p>Queremos dar la bienvenida....</p>

¿Cómo modificar el <h1>?

const h1 = document.querySelector('h1'); h1.textContent = 'Nuevo texto';

Seleccionando elemento HTML por nombre

DOCUMENTO └─ html ├─ headtitle"Artean" └─ body ├─ h1"Bolsa de empleo..." └─ p"Queremos dar..."
document.querySelector('h1');

Seleccionando el elemento h1

document.querySelector('h1');
body ├─ h1"Bolsa de empleo de Cuatrovientos" └─ p"Queremos dar la..."

Obteniendo el texto

document.querySelector('h1').textContent; // → "Bolsa de empleo de Cuatrovientos"
textContent es una propiedad nativa del DOM.

Modificando texto y esperando al DOM

Modificando el texto

document.querySelector('h1').textContent = 'Primer empleo';
body ├─ h1"Primer empleo" ← modificado └─ p"Queremos dar la..."

El DOM debe estar cargado

Puede ser que JavaScript se ejecute antes de que se cargue el DOM.

DOMContentLoaded se dispara cuando el DOM está completamente cargado.
document.addEventListener('DOMContentLoaded', () => { document.querySelector('h1').textContent = 'Primer empleo'; });
Recuerda: Siempre usa este evento para manipular el DOM.

Usando JavaScript Moderno

Desarrollo de Interfaces Web

Usando JavaScript

Comenzando

<script> document.addEventListener('DOMContentLoaded', () => { console.log('JavaScript está listo'); }); </script>

Cambiando múltiples elementos a la vez

Documento HTML

<h1>Artean</h1> <h2>Primer empleo</h2> <p>Categorías.</p> <ul id="categories"> <li>Asir</li> <li>Dam</li> <li class="medio">MicroInfo</li> </ul>

¿Cómo cambiar cada <li>?

document.querySelectorAll('li') .forEach(li => li.textContent = 'DAW');

Cargando HTML en el DOM

body ├─ h1"Artean" ├─ h2"Primer empleo" ├─ p"Categorías." └─ ul id="categories" ├─ li"Asir" ├─ li"Dam" └─ li class="medio""MicroInfo"

Seleccionando todos los <li>

document.querySelectorAll('li');
body ├─ h1"Artean" ├─ h2"Primer empleo" ├─ p"Categorías." └─ ul id="categories" ├─ li"Asir" ← seleccionado ├─ li"Dam" ← seleccionado └─ li"MicroInfo" ← seleccionado

Modificando cada nodo de texto

document.querySelectorAll('li').forEach(li => { li.textContent = 'DAW'; });
ul id="categories" ├─ li"DAW" ├─ li"DAW" └─ li"DAW"

Selectores por ID y clase

CSSJavaScript Moderno
.offers {..}document.querySelectorAll(".offers")
#container {..}document.querySelector("#container")
p {..}document.querySelectorAll("p")

Buscando por ID

¿Cómo seleccionamos específicamente el <ul> que tiene un ID "categories"?

document.querySelector("#categories");

Selector por ID en el DOM

body ├─ h1"Artean" ├─ h2"Primer empleo" ├─ p"Categorías." └─ ul id="categories" ← seleccionado ├─ li"Asir" ├─ li"Dam" └─ li class="medio""MicroInfo"

Buscando por clase

¿Cómo seleccionamos únicamente el <li> que tiene la clase "medio"?

document.querySelectorAll(".medio");

Selector por clase en el DOM

body ├─ h1"Artean" ├─ h2"Primer empleo" ├─ p"Categorías." └─ ul id="categories" ├─ li"Asir" ├─ li"Dam" └─ li class="medio""MicroInfo" ← seleccionado

Buscando en el DOM

Desarrollo de Interfaces Web

Selector descendiente

¿Cómo encontrar los <li> dentro de la <ul> con ID "categories"?

document.querySelectorAll("#categories li");
El espacio importa: #categories li selecciona todos los li descendientes.

Selector descendiente en el DOM

body ├─ h1"Artean" ├─ h2"Primer empleo" └─ ul id="categories" ├─ li"Asir" ├─ li"Dam" └─ li"MicroInfo"
document.querySelectorAll( "#categories li" );

Selector descendiente — Sub-listas

document.querySelectorAll("#categories li");

Selecciona TODOS los li descendientes, incluyendo los de sub-listas anidadas.

Problema: demasiados descendientes

ul id="categories" ├─ li"Asir" ← seleccionado ├─ li ← seleccionado (no queremos) │ └─ ul id="desarrollo" │ └─ li"Dam" ← NO queremos este └─ li"MicroInfo" ← seleccionado

Selector hijo (directo)

¿Cómo encontrar solo los <li> hijos directos de "categories"?

document.querySelectorAll("#categories > li");
El símbolo > importa: Solo selecciona hijos directos, no todos los descendientes.

Selector hijo en el DOM

ul id="categories" ├─ li"Asir" ← seleccionado (hijo directo) ├─ li ← seleccionado (hijo directo) │ └─ ul id="desarrollo" │ └─ li"Dam" ← NO seleccionado └─ li"MicroInfo" ← seleccionado (hijo directo)

Selector múltiple

¿Cómo encontrar elementos con clase "medio" O ID "desarrollo"?

document.querySelectorAll(".medio, #desarrollo");
La coma importa: Permite combinar múltiples selectores.

Selector múltiple en el DOM

ul id="categories" ├─ li"Asir" ├─ li │ └─ ul id="desarrollo" ← seleccionado │ └─ li"Dam" └─ li class="medio""MicroInfo" ← seleccionado

Pseudo-clases estilo CSS

document.querySelector("#categories li:first-child"); document.querySelector("#categories li:last-child");
Cuidado: El índice comienza en 0.

Transversal

Desarrollo de Interfaces Web

Caminando por el DOM

¿Podemos encontrar todos los <li> que la lista "categories" contiene sin usando un selector descendente?

document.querySelector("#categories").querySelectorAll("li");

Transversal vs Selectores

document.querySelectorAll("#categories li"); document.querySelector("#categories").querySelectorAll("li");

Obteniendo el primer y último elemento

Primer elemento

document.querySelector("li"); // → primer li del DOM // O desde un contenedor: document.querySelector("ul").querySelector("li");

Último elemento

const lis = document.querySelectorAll("li"); lis[lis.length - 1]; // último elemento

Navegando entre hermanos

¿Cómo encontrar el elemento del medio si no hay filtro :first o :last?

const primero = document.querySelector("li"); primero.nextElementSibling; // siguiente hermano primero.nextElementSibling.previousElementSibling; // anterior

Siguiente hermano — .nextElementSibling

document.querySelector("li").nextElementSibling;

Siguiente y Anterior hermano

const li = document.querySelector("li"); li.nextElementSibling; // siguiente li.nextElementSibling.previousElementSibling; // vuelve

Navegación completa entre hermanos

.nextElementSibling .previousElementSibling .parentElement.children

Transversal hacia arriba — .parentElement

Si empezamos seleccionando un elemento hijo, ¿podemos saber qué elemento es su padre?

document.querySelector("li").parentElement;

Obteniendo el padre

document.querySelector("li").parentElement;
body ├─ h1"Artean" ├─ h2"Primer empleo" └─ ul id="categories" ← parent ├─ li"Asir" ← hijo seleccionado ├─ li"Dam" └─ li"MicroInfo"

Transversal hacia abajo — .children

Con un padre que tiene muchos hijos con sus propios hijos, ¿cómo encontrar solo la primera generación?

document.querySelector("#categories").children; // Retorna solo hijos directos (HTMLCollection)

.children vs .querySelectorAll

// .children — solo hijos directos document.getElementById("categories").children; // .querySelectorAll — todos los descendientes document.querySelectorAll("#categories li");
children solo selecciona hijos directos, a diferencia de querySelectorAll que busca todos los descendientes.

Manipulando el DOM

Desarrollo de Interfaces Web

Ejemplo de oferta de empleo

Oferta de empleo — HTML renderizado

<li class="offer"> <h2>Código Limpito</h2> <p>Programador web</p> <button>Detalles</button> </li>

Añadiendo al DOM

Documento

DOCUMENTO └─ li class="offer" ├─ h2"Código Limpito" ├─ p"Programador web" └─ button"Detalles"

Operaciones

  • Añadir un nuevo nodo al DOM
  • Quitar un nodo del DOM

Creando nodos

const duties = document.createElement('p'); duties.textContent = 'Función: Programador web';
Crea un nodo pero aún no está en el DOM.

Métodos de inserción

.appendChild(<elem>) .prepend(<elem>) .after(<elem>) .before(<elem>)

.before() — Antes de

document.querySelector('.offer').before(duties);

Establece el nodo antes de .offer

.after() — Después de

document.querySelector('.offer').after(duties);

Establece el nodo después de .offer

.prepend() — Al principio

document.querySelector('.offer').prepend(duties);

Establece el nodo lo más arriba dentro de .offer

Prepend y Append

.prepend()

Al principio del elemento padre

.appendChild()

Al final del elemento padre

document.querySelector('.offer').appendChild(duties);

Borrando del DOM — .remove()

document.querySelector('button').remove();

Elimina el <button> del DOM

Oferta de empleo — Resultado

<li class="offer"> <h2>Código Limpito</h2> <p>Función: Programador web</p> <p>Programador web</p> <!-- button eliminado --> </li>

Métodos alternativos de inserción

// Usar los mismos métodos: .appendChild() .prepend() .after() .before()

Actuando ante las interacciones

Desarrollo de Interfaces Web

Lista de ofertas de empleo

Pasando una función como callback

document.addEventListener('DOMContentLoaded', () => { // código aquí });

Oferta de empleo — Con botón interactivo

<li class="offer"> <h2>Código Limpito</h2> <button>Detalles</button> </li>

El botón mostrará el detalle al hacer click.

Manejo de eventos — .addEventListener()

document.querySelector('button') .addEventListener('click', () => { // al hacer click });

Combinando DOMContentLoaded y eventos

document.addEventListener('DOMContentLoaded', () => { document.querySelector('button') .addEventListener('click', () => { // ejecuta al pulsar botón }); });

Ejemplo completo

document.addEventListener('DOMContentLoaded', () => { document.querySelector('button') .addEventListener('click', () => { const duties = document.createElement('p'); duties.textContent = 'Función: Programador web'; document.querySelector('.offer').appendChild(duties); document.querySelector('button').remove(); }); });

Resultado — Detalle al hacer click

<li class="offer"> <h2>Código Limpito</h2> <p>Función: Programador web</p> <!-- botón eliminado tras click --> </li>

El detalle se muestra cuando se hace click sobre el botón.

¿Qué pasa si hay múltiples ofertas?

<ul id="offers"> <li class="offer"> <h3>Código Limpito</h3> <button>Detalles</button> </li> <li class="offer"> <h3>Escuela Internacional...</h3> <button>Detalles</button> </li> </ul>

Problema: Todos los botones se eliminan

document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.offer').forEach(offer => { offer.appendChild(salary); // ¡Añade a TODAS! }); btn.remove(); // Solo elimina este botón }); });

Introducción a this

En caso de hacer click, el botón será this.

this.remove();
this se refiere al elemento que disparó el evento.

Usando this — Solo eliminar el botón clickeado

document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', function() { const salary = document.createElement('p'); salary.textContent = 'Salario: 399.99€'; this.closest('.offer').appendChild(salary); this.remove(); }); });

Resultado — Solo se elimina el botón clickeado

El botón que fue pulsado será eliminado, los demás permanecen.

Transversal con this.after()

this.after(salary); this.remove();

Añade el elemento <p> después del <button>.

.after() en contexto

ul id="offers" └─ li class="offer" ├─ h2"Código Limpito" ├─ button"Detalles" ← this └─ p"399.99€" ← añadido con .after()

¿Qué pasa si se mueve el botón?

Si el <button> se mueve, el precio se mueve con él.

¿Cómo mantener el precio como hijo del <li>?

this.closest('.offer').appendChild(salary);

Usando .closest()

this.closest('.offer').appendChild(salary);
.closest() busca el ancestro más cercano que coincida con el selector.

Nuestro capturador final

document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', function() { const salary = document.createElement('p'); salary.textContent = 'Salario: 399.99€'; this.closest('.offer').appendChild(salary); this.remove(); }); }); });

Resultado — El salario se añade correctamente

ul id="offers" └─ li class="offer" ├─ h2"Código Limpito" └─ p"Salario: 399.99€" ← correctamente añadido

Transversal y filtrando

Desarrollo de Interfaces Web

¿Cómo permitir salarios diferentes?

Vamos a usar atributos data- para guardar el salario de cada oferta.

Atributos data-

<li class="offer ondemand" data-salary="399.99"> <h3>Escuela Internacional...</h3> <button>Detalle salario</button> <ul class="functions"> <li>Diseño gráfico</li> <li>Tratamiento de fotografías</li> </ul> </li>
document.querySelector('.offer').dataset.salary; // → "399.99"

Leyendo desde data-

const amount = this.closest('.offer').dataset.salary; const price = document.createElement('p'); price.textContent = `Salario: ${amount}€`;

Cada oferta con su propio salario

document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', function() { const amount = this.closest('.offer').dataset.salary; const salary = document.createElement('p'); salary.textContent = `Salario: ${amount}€`; this.closest('.offer').appendChild(salary); this.remove(); }); }); });

Reutilizando objetos

const offer = this.closest('.offer'); const amount = offer.dataset.salary; offer.appendChild(document.createElement('p')).textContent = `${amount}€`;

Código optimizado — Reutilizando

document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('button').forEach(btn => { btn.addEventListener('click', function() { const offer = this.closest('.offer'); const amount = offer.dataset.salary; const salary = document.createElement('p'); salary.textContent = `Salario: ${amount}€`; offer.appendChild(salary); this.remove(); }); }); });
Solo realizamos la consulta una vez al DOM para este elemento.

Resultado — Cada oferta tiene su propio salario

ul id="offers" ├─ li class="offer" data-salary="399.99" │ ├─ h3"Código Limpito" │ └─ p"Salario: 399.99€" └─ li class="offer" data-salary="500.00" ├─ h3"Escuela Internacional..." └─ p"Salario: 500.00€"

Delegación de eventos

Si añadimos botones en cualquier lugar, sus eventos click serán gestionados.

document.querySelector('.offer') .addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON') { // lógica del botón } });

Implementando filtros

A continuación implementaremos filtros para resaltar ofertas.

Filtrando HTML

<div id="filters"> <button class="ondemand-filter">En demanda ahora</button> <button class="expiring-filter">Caducando</button> </div>

Vamos a desarrollar dos capturadores de eventos para nuestros botones.

Filtrando ofertas ondemand

document.querySelector('#filters').addEventListener('click', (e) => { if (e.target.classList.contains('ondemand-filter')) { document.querySelectorAll('.offer.ondemand') .forEach(offer => offer.classList.add('highlighted')); } });

.filter() vs .querySelectorAll()

document.querySelectorAll('.offer.ondemand');
En CSS: .offer.ondemand selecciona elementos con ambas clases.

.addClass() y .removeClass()

document.querySelectorAll('.offer.ondemand') .forEach(el => el.classList.add('highlighted')); document.querySelectorAll('.offer.ondemand') .forEach(el => el.classList.remove('highlighted'));

Filtro para expiring

// Filtro ondemand document.querySelector('#filters').addEventListener('click', (e) => { if (e.target.classList.contains('ondemand-filter')) { document.querySelectorAll('.offer.ondemand') .forEach(el => el.classList.add('highlighted')); } }); // Filtro expiring — mismo patrón if (e.target.classList.contains('expiring-filter')) { document.querySelectorAll('.offer.expiring') .forEach(el => el.classList.add('highlighted')); }

¿Cómo no resaltar todas las ofertas?

Necesitamos eliminar la clase highlighted antes de volver a asignarla.

Solución: Eliminar antes de reasignar

document.querySelector('#filters').addEventListener('click', (e) => { if (e.target.classList.contains('ondemand-filter')) { // Primero eliminar de todos document.querySelectorAll('.highlighted') .forEach(el => el.classList.remove('highlighted')); // Luego añadir a los filtrados document.querySelectorAll('.offer.ondemand') .forEach(el => el.classList.add('highlighted')); } });

Resultado — Solo se resalta la oferta seleccionada

Al hacer click en un filtro, se elimina el resaltado anterior y se aplica solo a las ofertas que cumplen la condición.

Resumen: JavaScript moderno nativo puede hacer todo lo necesario, sin dependencias externas.
1 / 98
UT03 - JavaScript Moderno