Interactividad con Alpine.js

En esta página

Interactividad con Alpine.js

Cada página construida por BxSites ya carga Alpine.js

  • es lo que impulsa el interruptor de modo oscuro y el desplegable de idioma incorporados en todos los temas incluidos. Esa misma instancia de Alpine también está disponible para el contenido de tus propias páginas, gratis: sin ninguna opción de bxsites.yaml que activar, sin ninguna entrada extraJs que añadir, sin ninguna etiqueta <script> adicional que escribir en tu markdown.

Dado que el HTML en bruto a nivel de bloque pasa sin cambios en tu markdown, puedes colocar los atributos x-data/x-show/@click/ etc. de Alpine directamente en cualquier bloque HTML y simplemente funciona.

Antes de recurrir a Alpine

La mayoría de las necesidades "interactivas" ya tienen un bloque de directiva construido para ese propósito que no requiere escribir nada de JS por tu cuenta - recurre primero a estos:

  • Una sección colapsable → Expandible o una admonición colapsable
  • Contenido alternativo agrupado detrás de pestañas clicables → Pestañas de Contenido
  • Un recorrido numerado paso a paso → Stepper
  • Un enlace estilizado de llamada a la acción → Botones (el botón de copiar al portapapeles de abajo es un caso diferente - no tiene ningún href en absoluto, solo comportamiento del lado del cliente - que es exactamente para lo que sirve Alpine)

Alpine es para el contenido interactivo que esos no cubren - cualquier cosa con su propio estado del lado del cliente.

Un botón de copiar al portapapeles

::: button únicamente renderiza un enlace real (o un marcador de posición inerte) - al igual que el propio botón de GitBook, no tiene ninguna noción de ejecutar JS arbitrario al hacer clic. Para un botón que sí hace algo en lugar de navegar a algún sitio, coloca sus clases bxsites-button/bxsites-button--* en un <button> HTML simple en su lugar - el mismo aspecto, con estilos en todos los temas incorporados, solo que conectado con Alpine en lugar de un href. Uno común: un botón junto a un comando de instalación que lo copia y confirma la copia:

<div x-data="{ copied: false }">
  <button type="button" class="bxsites-button bxsites-button--secondary bxsites-button--small"
    @click="navigator.clipboard.writeText( 'box install bx-sites' ); copied = true; setTimeout( () => copied = false, 1500 )">
    <span x-show="!copied">Copy install command</span>
    <span x-show="copied" x-cloak>Copied!</span>
  </button>
</div>

Un filtro en vivo

Filtrar una lista del lado del cliente, sin ida y vuelta al servidor:

<div x-data="{ query: '' }">
  <input type="text" x-model="query" placeholder="Filter providers...">
  <ul>
    <li x-show="'local'.includes( query.toLowerCase() )">local (static index, no server)</li>
    <li x-show="'algolia'.includes( query.toLowerCase() )">algolia (hosted DocSearch)</li>
    <li x-show="'pagefind'.includes( query.toLowerCase() )">pagefind (indexed at build time)</li>
  </ul>
</div>

x-model vincula el valor del campo al estado de Alpine; el x-show de cada <li> se reevalúa en cada pulsación de tecla.

Una tabla ordenable y filtrable

Una tabla de pipes nativa es estática una vez construida - para una que el lector realmente pueda ordenar y filtrar del lado del cliente (lo más parecido que hay aquí a la búsqueda/orden de tablas de GitBook), deja que Alpine sea el dueño de las filas en su lugar: coloca los datos en x-data y renderízalos con x-for, en lugar de escribir la sintaxis de pipes | Feature | Status |:

<div x-data="{
  query: '',
  sortKey: 'name',
  sortAsc: true,
  rows: [
    { name: 'Bootstrap', type: 'Components', stars: 4 },
    { name: 'GitBook', type: 'SaaS', stars: 5 },
    { name: 'Docusaurus', type: 'React', stars: 4 },
    { name: 'VuePress', type: 'Vue', stars: 3 }
  ],
  sortBy(key) {
    this.sortAsc = this.sortKey === key ? !this.sortAsc : true
    this.sortKey = key
  },
  get sorted() {
    return [...this.rows]
      .filter(r => r.name.toLowerCase().includes(this.query.toLowerCase()))
      .sort((a, b) => {
        const dir = this.sortAsc ? 1 : -1
        return a[this.sortKey] > b[this.sortKey] ? dir : a[this.sortKey] < b[this.sortKey] ? -dir : 0
      })
  }
}">
  <input type="text" x-model="query" placeholder="Filter by name...">
  <table class="table">
    <thead>
      <tr>
        <th @click="sortBy('name')" style="cursor:pointer">Name</th>
        <th @click="sortBy('type')" style="cursor:pointer">Type</th>
        <th @click="sortBy('stars')" style="cursor:pointer">Stars</th>
      </tr>
    </thead>
    <tbody>
      <template x-for="row in sorted" :key="row.name">
        <tr>
          <td x-text="row.name"></td>
          <td x-text="row.type"></td>
          <td x-text="row.stars"></td>
        </tr>
      </template>
    </tbody>
  </table>
</div>

Lo que se renderiza como (escribe en el cuadro, haz clic en un encabezado de columna):

Name Type Stars

rows es un array JS plano incrustado directamente en la página - bien para el tipo de tabla de referencia pequeña que suelen tener los docs. sorted es un getter de Alpine, así que vuelve a filtrar y a ordenar en cada pulsación de tecla/clic sin cableado adicional; sortBy() invierte la dirección al hacer un segundo clic en la misma columna. El <table> de aquí es una etiqueta <table> real escrita a mano (no hay sintaxis de tabla de pipes que entregue filas a Alpine directamente), así que de todas formas se envuelve en .bxsites-table-wrap y recibe automáticamente el tratamiento de desplazamiento responsivo/encabezado fijo, igual que cualquier tabla que el propio bx-markdown renderiza.

Fundamentos de x-data, si eres nuevo en Alpine

x-data declara el propio estado reactivo de un ámbito como un objeto JS simple; cualquier cosa dentro de ese elemento puede leerlo/escribirlo, y x-show/x-text/x-model/@click (abreviatura de x-on:click) reaccionan todos cuando cambia:

<div x-data="{ count: 0 }">
  <button type="button" @click="count++">Clicked <span x-text="count"></span> times</button>
</div>

Consulta la propia documentación de Alpine para la lista completa de directivas (x-if, x-for, x-transition, y más).

Cosas que debes saber

  • Es parte del núcleo, no opcional. El armazón del tema (modo oscuro, selector de idioma) depende de Alpine, así que no se puede desactivar en bxsites.yaml como sí puede hacerse con mermaid/math.
  • Versión. Actualmente alpinejs@3.14.1, incluida con este módulo y servida desde site/assets/vendor/alpine/ - sin ningún CDN involucrado. Consulta el propio layout.bxm de un tema para ver la etiqueta <script> exacta si necesitas saber precisamente qué se carga.
  • CSP estricta. La compilación por defecto de Alpine evalúa las expresiones JS dentro de x-data/@click etc. directamente, lo cual necesita unsafe-eval bajo una Content-Security-Policy estricta. Si tu despliegue no puede permitir eso, no dependas de Alpine en el contenido de tus páginas.
  • Mantenlo ligero. Una página de documentación debe seguir siendo rápida y sencilla - pequeños widgets autocontenidos (un botón de copiar, un filtro, un interruptor) encajan bien; una aplicación completa del lado del cliente no es para lo que sirve esto.
Editar esta página Descargar Markdown Última actualización Aug 28, 2026, 3:16:38 AM