Infinite Scroll Componentes
Quando a pessoa chega perto do fim da lista, ele busca a próxima página no seu servidor e acrescenta embaixo — sem botão "carregar mais".
O que é
É o que o Instagram e os blogs fazem: você rola, e mais posts aparecem sozinhos. Você marca a área com sw-infinite, diz de onde buscar (sw-infinite-url, um endereço do seu site que devolve um pedaço de HTML) e onde colocar (sw-infinite-target). O resto é automático.
Precisa do JavaScript do SWCore e de um endereço no seu servidor que devolva o HTML dos próximos itens. O script vigia a rolagem: quando falta menos que sw-infinite-offset pixels para o fim, ele busca a URL (trocando {page} pelo número da página), mostra "Carregando…" e acrescenta o que veio no alvo. Quando o servidor devolver uma resposta vazia, ele entende que acabou e para. Os componentes SWCore que vierem no HTML novo já chegam funcionando.
Quando usar
- Blog, notícias, galeria de fotos: a pessoa só quer continuar rolando.
- Lista longa em painel onde paginar com números atrapalha (histórico, mensagens).
- Não use em página que tem rodapé importante (contato, endereço, política): ele nunca chega a aparecer. Nesse caso, pagine com números (Paginação) ou um botão "Ver mais".
Comece aqui
-
1
Coloque o SWCore na página (o JS é obrigatório).
<link rel="stylesheet" href="https://swcore.sanweb.com.br/dist/2.1.0/swcore.min.css"> <!-- fim do body --> <script src="https://swcore.sanweb.com.br/dist/2.1.0/swcore.min.js"></script> -
2
Crie no seu servidor um endereço que devolva só o HTML dos itens de uma página (sem
<html>, sem cabeçalho). Ex.:/posts.php?page=2devolve:<article class="post">Post 11</article> <article class="post">Post 12</article> <article class="post">Post 13</article> <!-- página que não existe mais: devolva uma resposta VAZIA — é assim que ele sabe que acabou --> -
3
Na página, marque a lista e diga de onde buscar e onde colocar. Se a página já mostra a página 1, comece a busca pela 2 com
sw-infinite-start="2".<div sw-infinite sw-infinite-url="/posts.php?page={page}" sw-infinite-target="#lista-posts" sw-infinite-start="2"> <div id="lista-posts"> <article class="post">Post 1</article> <!-- … os posts da página 1, vindos do servidor … --> </div> </div>
Todas as variações
Cada quadro abaixo é o componente de verdade, rodando. O código embaixo é exatamente o que está no quadro: copie e cole.
1. Rolando dentro de uma caixa
Estes quadros buscam de verdade um arquivo de exemplo deste site (assets/demo-infinite.html, que sempre devolve 4 posts, então nunca "acaba"). Role até o fim da caixa. Se a própria [sw-infinite] tiver rolagem (aqui, ovy + altura pela proporção ar-21-9), ele usa essa rolagem sozinho. sw-infinite-offset diz com quantos pixels de antecedência ele começa a buscar (padrão 300).
Post 1 — já veio com a página
Post 2 — já veio com a página
Post 3 — já veio com a página
Post 4 — já veio com a página
Post 5 — já veio com a página
Post 6 — já veio com a página
Post 7 — já veio com a página
Post 8 — já veio com a página
Post 9 — já veio com a página
Post 10 — já veio com a página
Post 11 — já veio com a página
Post 12 — já veio com a página
Post 13 — já veio com a página
Post 14 — já veio com a página
<div sw-infinite sw-infinite-url="../../assets/demo-infinite.html" sw-infinite-target="#inf-demo-1" sw-infinite-offset="40" class="ovy ar-21-9 bd r-m p-2">
<div id="inf-demo-1">
<p>Post 1 — já veio com a página</p>
<p>Post 2 — já veio com a página</p>
<p>Post 3 — já veio com a página</p>
<p>Post 4 — já veio com a página</p>
<p>Post 5 — já veio com a página</p>
<p>Post 6 — já veio com a página</p>
<p>Post 7 — já veio com a página</p>
<p>Post 8 — já veio com a página</p>
<p>Post 9 — já veio com a página</p>
<p>Post 10 — já veio com a página</p>
<p>Post 11 — já veio com a página</p>
<p>Post 12 — já veio com a página</p>
<p>Post 13 — já veio com a página</p>
<p>Post 14 — já veio com a página</p>
</div>
</div>
Post 1 — já veio com a página
Post 2 — já veio com a página
Post 3 — já veio com a página
Post 4 — já veio com a página
Post 5 — já veio com a página
Post 6 — já veio com a página
Post 7 — já veio com a página
Post 8 — já veio com a página
Post 9 — já veio com a página
Post 10 — já veio com a página
Post 11 — já veio com a página
Post 12 — já veio com a página
Post 13 — já veio com a página
Post 14 — já veio com a página
<div sw-infinite sw-infinite-url="../../assets/demo-infinite.html" sw-infinite-target="#inf-demo-2" class="ovy ar-21-9 bd r-m p-2">
<div id="inf-demo-2">
<p>Post 1 — já veio com a página</p>
<p>Post 2 — já veio com a página</p>
<p>Post 3 — já veio com a página</p>
<p>Post 4 — já veio com a página</p>
<p>Post 5 — já veio com a página</p>
<p>Post 6 — já veio com a página</p>
<p>Post 7 — já veio com a página</p>
<p>Post 8 — já veio com a página</p>
<p>Post 9 — já veio com a página</p>
<p>Post 10 — já veio com a página</p>
<p>Post 11 — já veio com a página</p>
<p>Post 12 — já veio com a página</p>
<p>Post 13 — já veio com a página</p>
<p>Post 14 — já veio com a página</p>
</div>
</div>
Tabela de opções
| Palavra | O que faz | Exemplo |
|---|---|---|
| Obrigatórios | ||
sw-infinite | Marca a área. Pode ser a própria caixa que rola, ou um bloco dentro da página. | <div sw-infinite …> |
sw-infinite-url | Endereço que devolve o HTML dos próximos itens. {page} é trocado pelo número da página. | sw-infinite-url="/posts.php?page={page}" |
sw-infinite-target | Onde o HTML novo entra (seletor CSS). Sem ele, entra no fim do <body> — quase nunca é o que você quer. | sw-infinite-target="#lista-posts" |
| Ajustes | ||
sw-infinite-start | Número da primeira página que ele busca. Padrão: 1. Se a página já traz a 1, use 2. | sw-infinite-start="2" |
sw-infinite-trusted | Pula a limpeza do HTML que chega (tira <script>, onclick="" e links javascript:). Só para conteúdo 100% seu, nunca com texto escrito por usuário. | sw-infinite-trusted |
sw-infinite-offset | Quantos pixels antes do fim ele começa a buscar. Padrão: 300. | sw-infinite-offset="600" |
sw-infinite-root | Seletor da caixa que rola, quando não é a própria [sw-infinite] nem a página. | sw-infinite-root="#coluna" |
| Quem rola (ele descobre sozinho) | ||
(sem sw-infinite-root) | Se a [sw-infinite] (ou uma caixa acima dela) tem rolagem e está cheia, usa essa. Senão, usa a rolagem da página. | |
| Aparência | ||
.sw-infinite-loader | O aviso "Carregando…" que aparece no fim do alvo durante a busca. Mude o visual no CSS da sua página, se quiser. | |
Receitas prontas — usos reais
Situações que aparecem em quase todo site. Copie a que servir.
<main>
<div sw-infinite
sw-infinite-url="/blog/pagina/{page}"
sw-infinite-target="#posts"
sw-infinite-start="2"
sw-infinite-offset="600">
<div id="posts">
<!-- posts da página 1, já vindos do servidor (bom para o Google) -->
</div>
</div>
</main>
<!-- a <main> não tem rolagem própria: ele usa a rolagem da página -->
<?php
// /posts.php?page=2
$porPagina = 10;
$pagina = max(1, (int) ($_GET['page'] ?? 1));
$offset = ($pagina - 1) * $porPagina;
$stmt = $pdo->prepare('SELECT titulo, resumo FROM posts ORDER BY id DESC LIMIT ? OFFSET ?');
$stmt->bindValue(1, $porPagina, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->execute();
header('Content-Type: text/html; charset=utf-8');
foreach ($stmt as $p) {
echo '<article class="post"><h2>' . htmlspecialchars($p['titulo']) . '</h2>'
. '<p>' . htmlspecialchars($p['resumo']) . '</p></article>';
}
// sem linhas = resposta vazia = o componente para de buscar
Post 1 — já veio com a página
Post 2 — já veio com a página
Post 3 — já veio com a página
Post 4 — já veio com a página
Post 5 — já veio com a página
Post 6 — já veio com a página
Post 7 — já veio com a página
Post 8 — já veio com a página
Post 9 — já veio com a página
Post 10 — já veio com a página
Post 11 — já veio com a página
Post 12 — já veio com a página
Post 13 — já veio com a página
Post 14 — já veio com a página
Role a caixa: aqui aparece o que o evento trouxe.
<div sw-infinite id="posts-box" sw-infinite-url="/posts.php?page={page}" sw-infinite-target="#posts">…</div>
<p id="fim-lista" hidden>Você viu tudo.</p>
<script>
const box = document.getElementById('posts-box');
box.addEventListener('sw:infinite:load', (e) => {
// e.detail.items = os elementos que chegaram; e.detail.page = a página que acabou de chegar
console.log(e.detail.items.length, 'itens novos');
});
box.addEventListener('sw:infinite:end', () => {
document.getElementById('fim-lista').hidden = false;
});
box.addEventListener('sw:infinite:error', (e) => {
SW.Alert.err('Não foi possível carregar a página ' + e.detail.page);
});
</script>
Eventos
sw:infinite:loade.detail.items (lista dos elementos novos) e e.detail.page (o número da página que acabou de chegar).sw:infinite:endsw:infinite:errore.detail.page. Ele tenta de novo na próxima rolagem.API (JavaScript)
SW.Infinite.initAll(raiz)[sw-infinite] dentro de raiz. Normalmente não precisa: o SWCore liga sozinho.Precisa de quê
swcore.min.csseswcore.min.js.- Um endereço no seu servidor que devolva o HTML dos itens de cada página (texto HTML, não JSON) e uma resposta vazia quando não houver mais.
- Se o endereço for de outro domínio, ele precisa liberar o seu site (CORS). O mais simples é ficar no mesmo domínio.
Cuidados — erros comuns
- Esquecer o
sw-infinite-target: o HTML novo vai parar no fim do<body>, depois do rodapé. - A página já mostra a página 1 e você não pôs
sw-infinite-start="2": a primeira busca traz a página 1 de novo e os posts aparecem repetidos. - Servidor que devolve a página inteira (com
<html>, menu, rodapé): tudo isso é colado na lista. Devolva só os itens. - Servidor que nunca devolve vazio (ex.: repete a última página): ele busca para sempre. Na última página, responda sem conteúdo.
- Caixa com rolagem própria mas sem altura: ela cresce com o conteúdo, não rola, e ele passa a vigiar a rolagem da página.
- Apontar para um endereço que devolve JSON: ele cola o texto do JSON na tela. Para JSON, use o Conteúdo via JSON.
O que ele não faz
- Não monta HTML a partir de JSON — o servidor precisa mandar o HTML pronto.
- O HTML que chega passa por uma limpeza (a mesma do
sw-ajax):<script>, atributoson…=""e linksjavascript:são removidos. Mesmo assim, escape no servidor o texto que o usuário escreveu (como ohtmlspecialcharsda receita). - Não tem botão "carregar mais" nem função para forçar a próxima busca por código: ela acontece pela rolagem.
- Se as primeiras páginas não enchem a caixa, ele só busca de novo quando houver rolagem. Mande itens suficientes por página para passar da altura da tela.
- O rodapé da página fica inalcançável enquanto houver páginas para carregar.

