Construindo Scroll Infinito com HTMX
Carregar conteúdo conforme os usuários rolam a página elimina cliques de paginação e cria experiências de navegação mais fluidas. Mas implementar scroll infinito tradicionalmente requer gerenciar intersection observers, rastrear estado e escrever JavaScript considerável. HTMX oferece uma abordagem mais simples: scroll infinito orientado pelo servidor usando atributos HTML.
Este guia cobre o padrão canônico de scroll infinito do HTMX, explica quando usar revealed versus intersect, e mostra como construir uma implementação progressivamente aprimorada que funciona sem JavaScript.
Pontos-Chave
- O scroll infinito do HTMX usa um padrão de elemento carregador auto-perpetuante onde cada resposta do servidor inclui o próximo carregador
- Use
revealedpara rolagem de página completa eintersect oncepara contêineres roláveis com overflow - Construa sobre links de paginação padrão para aprimoramento progressivo que funciona sem JavaScript
- O servidor controla todo o estado de paginação através do elemento carregador em cada resposta
Como Funciona o Scroll Infinito Orientado pelo Servidor
O padrão central coloca atributos HTMX em um elemento “carregador”—tipicamente o último item da sua lista. Quando este elemento se torna visível, o HTMX solicita a próxima página do seu servidor. O servidor retorna HTML contendo tanto os novos itens quanto um novo elemento carregador apontando para a página subsequente.
Aqui está a estrutura básica:
<div id="items">
<div class="item">First item</div>
<div class="item">Second item</div>
<!-- Last rendered item doubles as the loader -->
<div class="item"
hx-get="/items?page=2"
hx-trigger="revealed"
hx-swap="afterend">
Last item of page 1
</div>
</div>
A resposta do servidor para a página 2 inclui itens e um novo carregador:
<div class="item">First item of page 2</div>
<div class="item">Second item of page 2</div>
<div class="item"
hx-get="/items?page=3"
hx-trigger="revealed"
hx-swap="afterend">
Last item of page 2
</div>
Este padrão auto-perpetuante continua até que o servidor retorne conteúdo sem um carregador, sinalizando o fim dos dados disponíveis.
HTMX Intersect vs Revealed: Escolhendo o Gatilho Correto
O HTMX fornece dois gatilhos baseados em visibilidade, e selecionar o errado causa bugs comuns.
Use revealed para rolagem de página completa onde o próprio documento rola. Este gatilho dispara quando um elemento entra no viewport do navegador.
Use intersect once quando o conteúdo está dentro de um contêiner rolável com CSS como overflow-y: scroll. O gatilho revealed observa o viewport do documento, não elementos roláveis individuais, então não disparará corretamente em contêineres com overflow. O gatilho intersect depende da moderna Intersection Observer API, que é suportada em todos os principais navegadores.
<!-- Full-page scroll: use revealed -->
<div hx-trigger="revealed" hx-get="/more">...</div>
<!-- Scrollable container: use intersect once -->
<div class="scroll-container" style="overflow-y: scroll; height: 400px;">
<div hx-trigger="intersect once" hx-get="/more">...</div>
</div>
O modificador once previne requisições duplicadas se o elemento repetidamente entrar e sair do limite de interseção.
Estratégias de Swap: afterend vs beforeend
Sua estratégia de swap determina onde o novo conteúdo aparece e como prevenir carregamentos duplicados.
afterend insere conteúdo imediatamente após o elemento carregador. Use isso quando o carregador é o último item renderizado—a resposta aparece abaixo dele.
beforeend anexa conteúdo dentro de um contêiner alvo. Combine isso com hx-target para especificar onde os itens vão:
<div hx-get="/items?page=2"
hx-trigger="revealed"
hx-target="#items"
hx-swap="beforeend">
Loading indicator...
</div>
Com beforeend, seu carregador tipicamente fica fora do contêiner de itens e é substituído ou removido após a página final carregar.
Discover how at OpenReplay.com.
Aprimoramento Progressivo com Paginação Padrão
O scroll infinito com HTMX deve ser construído sobre a paginação existente, não substituí-la inteiramente. Comece com links de paginação funcionais:
<div id="items">
{% for item in items %}
<div class="item">{{ item.name }}</div>
{% endfor %}
</div>
<a href="/items?page={{ next_page }}">Load more</a>
Então atualize o link de paginação com atributos HTMX:
<a href="/items?page={{ next_page }}"
hx-get="/items?page={{ next_page }}"
hx-trigger="revealed"
hx-target="#items"
hx-swap="beforeend"
hx-select="#items > *">
Load more
</a>
Usuários sem JavaScript veem paginação padrão. Usuários com HTMX obtêm scroll infinito. O endpoint do servidor permanece idêntico para ambos.
Atualizando Elementos de UI com Swaps Out-of-Band
Às vezes você precisa atualizar elementos fora da área de conteúdo principal—como contadores de itens ou estados de carregamento. Os out-of-band swaps do HTMX lidam com isso:
<!-- In server response -->
<div class="item">New item</div>
<span id="item-count" hx-swap-oob="outerHTML">Showing 20 of 100</span>
O atributo hx-swap-oob="outerHTML" diz ao HTMX para encontrar e substituir o elemento com ID correspondente em qualquer lugar da página, independentemente do alvo de swap principal.
Estratégias de Paginação do Lado do Servidor
Seu servidor controla o estado de paginação inteiramente. Duas abordagens comuns:
Paginação por offset usa números de página ou valores de offset: /items?page=3 ou /items?offset=20. Simples de implementar, mas pode mostrar duplicatas se itens forem adicionados durante a navegação.
Paginação por cursor usa um ponteiro para o último item visto: /items?after=item_xyz. Mais confiável para conteúdo dinâmico, mas requer identificadores estáveis.
Qualquer abordagem funciona com HTMX—o cliente simplesmente passa qualquer parâmetro que o servidor forneça no próximo elemento carregador.
Conclusão
O scroll infinito do HTMX move a complexidade para o servidor onde a lógica de paginação já existe. Escolha revealed para rolagem de documento e intersect once para contêineres com overflow. Construa sobre paginação padrão para que seu recurso degrade graciosamente. Deixe o servidor conduzir o estado através do elemento carregador de cada resposta em vez de rastrear páginas no lado do cliente.
Perguntas Frequentes
Isso geralmente acontece quando se usa intersect sem o modificador once. O elemento pode cruzar repetidamente o limite de interseção conforme o conteúdo carrega e o layout muda. Adicione once ao seu gatilho como hx-trigger intersect once para garantir que apenas uma única requisição dispare por elemento carregador.
Adicione um atributo hx-indicator apontando para seu elemento de carregamento. Por exemplo hx-indicator com um valor de hash loading no seu elemento carregador e um span separado com id loading contendo seu spinner. O HTMX automaticamente mostra este elemento durante requisições e o oculta quando completo.
O HTMX ajuda a prevenir requisições sobrepostas, e cada elemento carregador é tipicamente configurado para disparar uma vez. A resposta do servidor determina a próxima página. Se você precisar de coordenação mais rigorosa, pode usar hx-sync para controlar o comportamento das requisições.
Sim. Quando os filtros mudarem, redefina seu contêiner de conteúdo e atualize a URL do carregador para incluir parâmetros de filtro. O servidor lida com a lógica de filtragem e retorna a primeira página apropriada. Cada carregador subsequente inclui os mesmos parâmetros de filtro para manter consistência entre as páginas.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.