Targets y selectores extendidos
Targets y selectores extendidos
hx-target responde a dónde va la respuesta. Si no lo escribes, el destino es el propio
elemento que disparó la petición.
<!-- Se reemplaza a sí mismo -->
<button hx-get="/hora">¿Qué hora es?</button>
<!-- Reemplaza otro elemento -->
<button hx-get="/hora" hx-target="#reloj">¿Qué hora es?</button>
<div id="reloj"></div>
Selectores CSS estándar
Cualquier selector CSS válido funciona, y se resuelve sobre todo el documento:
<button hx-get="/datos" hx-target="#resultados">Por id</button>
<button hx-get="/datos" hx-target=".contenedor">Por clase (el primero que coincida)</button>
<button hx-get="/datos" hx-target="table tbody">Por descendencia</button>
<button hx-get="/datos" hx-target="[data-panel='ventas']">Por atributo</button>
Cuando el selector coincide con varios elementos, htmx usa el primero.
El problema de los ids únicos
En una lista generada por el servidor, escribir hx-target="#fila-42" te obliga a generar ids
únicos para todo. Funciona, pero se vuelve frágil: si el mismo componente aparece dos veces en la
página, los ids chocan.
Los selectores relativos resuelven eso: describen el destino en relación al elemento que dispara, no por su identidad.
flowchart TD
A["<div class='card'>"] --> B["<div class='card-body'>"]
B --> C["<button> ← dispara"]
B --> D["<span class='estado'>"]
A --> E["<footer>"]
C -.->|"closest .card"| A
C -.->|"find .estado"| B
C -.->|"next"| D
Tabla de selectores extendidos
| Selector | Qué apunta |
|---|---|
this | El propio elemento (el valor por defecto) |
closest <sel> | El ancestro más cercano que coincide (incluye el propio elemento) |
find <sel> | El primer descendiente que coincide |
findAll <sel> | Todos los descendientes que coinciden |
next | El hermano siguiente |
next <sel> | El primer hermano posterior que coincide |
previous | El hermano anterior |
previous <sel> | El primer hermano anterior que coincide |
global:<sel> | Selector CSS global, atravesando el shadow DOM |
closest
El más usado. Un botón que actualiza la tarjeta que lo contiene:
<div class="card">
<div class="card-body">
<h3>Pedido #481</h3>
<button hx-get="/pedidos/481" hx-target="closest .card" hx-swap="outerHTML">
Actualizar
</button>
</div>
</div>
El mismo HTML sirve para cien tarjetas: no hay ids que generar.
Aplicado a filas de tabla:
<tr>
<td>Ana</td>
<td>
<button hx-delete="/usuarios/7" hx-target="closest tr" hx-swap="delete">
Eliminar
</button>
</td>
</tr>
find y findAll
Buscan hacia abajo. Útil cuando el disparador es el contenedor:
<div class="widget" hx-get="/widget/datos" hx-trigger="load" hx-target="find .contenido">
<h4>Ventas del mes</h4>
<div class="contenido">Cargando…</div>
</div>
findAll inserta la respuesta en todos los descendientes que coincidan:
<div hx-get="/precio" hx-target="findAll .precio">
<span class="precio">—</span>
<span class="precio">—</span>
</div>
next y previous
Trabajan sobre hermanos. Sin selector, el hermano inmediato:
<button hx-get="/detalle/1" hx-target="next">Ver detalle</button>
<div></div>
Con selector, el primer hermano en esa dirección que coincida:
<button hx-get="/detalle/1" hx-target="next .panel">Ver detalle</button>
<span>texto que se salta</span>
<div class="panel"></div>
Es el patrón natural para acordeones y “mostrar más”:
<ul>
<li>
<button hx-get="/faq/1/respuesta" hx-target="next .respuesta">¿Cómo funciona?</button>
<div class="respuesta"></div>
</li>
<li>
<button hx-get="/faq/2/respuesta" hx-target="next .respuesta">¿Cuánto cuesta?</button>
<div class="respuesta"></div>
</li>
</ul>
global:
Los selectores de htmx se resuelven dentro del árbol donde vive el elemento. Si estás dentro de un
shadow DOM (un web component), #modal no encontrará un elemento del documento principal.
global: fuerza la búsqueda en el documento raíz:
<button hx-get="/aviso" hx-target="global:#notificaciones">Avisar</button>
Combinar target con selección de la respuesta
hx-target decide dónde va; hx-select decide qué parte de la respuesta se usa. Son
independientes y se combinan:
<button hx-get="/reporte-completo"
hx-select="#tabla-resumen"
hx-target="#panel">
Ver resumen
</button>
El servidor devuelve una página entera, htmx extrae sólo #tabla-resumen y lo inserta en #panel.
Es la manera de reutilizar páginas existentes como fuente de fragmentos sin tocar el backend.
El capítulo 8 profundiza en hx-select y hx-select-oob.
Reasignar el target desde el servidor
El servidor puede sobrescribir el target de una respuesta concreta con el header HX-Retarget:
// Si la validación falla, mandamos el error al panel de errores
// en vez de al target original
return new Response(`<p class="error">El RUT ya existe</p>`, {
status: 409,
headers: {
"Content-Type": "text/html",
"HX-Retarget": "#errores",
"HX-Reswap": "innerHTML",
},
});
Esto evita tener que declarar en el HTML todos los destinos posibles de una acción.
Herencia del target
En htmx 4 la herencia es explícita. Para que un contenedor imponga el target a todos sus hijos:
<div hx-target:inherited="#panel-principal">
<button hx-get="/a">A</button>
<button hx-get="/b">B</button>
<button hx-get="/c" hx-target="#otro">C — anula la herencia</button>
</div>
Sin :inherited, cada botón sin hx-target apuntaría a sí mismo. Este es el error número uno al
venir de htmx 2 y se cubre completo en el
capítulo 7.
Elegir el target correcto
flowchart TD
A{¿Qué actualizas?} --> B[El propio elemento]
A --> C[Un contenedor ancestro]
A --> D[Algo dentro del disparador]
A --> E[Un hermano]
A --> F[Una zona fija de la página]
B --> B1["hx-target='this' — o nada"]
C --> C1["hx-target='closest .card'"]
D --> D1["hx-target='find .contenido'"]
E --> E1["hx-target='next .panel'"]
F --> F1["hx-target='#panel'"]
Regla práctica: prefiere selectores relativos cuando el HTML se repite (listas, tablas, tarjetas) y selectores por id cuando la zona es única en la página (una barra lateral, un contador del header, un modal).
Errores comunes
| Síntoma | Causa | Solución |
|---|---|---|
| El contenido aparece dentro del botón | No hay hx-target y el default es this | Declara el target |
#fila-3 no existe al segundo clic | Un swap anterior borró el elemento | Usa closest tr en lugar del id |
| El target no se encuentra en un web component | Estás dentro de un shadow root | Usa global:#destino |
| Los hijos no heredan el target | htmx 4 no hereda por defecto | Usa hx-target:inherited |
next no encuentra nada | El elemento no es hermano sino sobrino | Usa closest + find |
Resumen
- Por defecto, el target es el propio elemento.
- Acepta cualquier selector CSS, resuelto sobre el documento.
- Los selectores relativos (
closest,find,findAll,next,previous) evitan generar ids únicos y hacen el HTML reutilizable. global:atraviesa el shadow DOM.hx-selectrecorta la respuesta;hx-targetelige el destino: son ortogonales.HX-Retargetpermite al servidor redirigir la respuesta a otro elemento.- La herencia del target requiere
hx-target:inheriteden htmx 4.
Siguiente: Swaps y morphing →.