Scroll Infinito

Tutorial de como montar uma página com scroll infinito e discute as precauções ao adotar este elemento de usabilidade.

Introdução

Um dos elementos de usabilidade que vem aparecendo em alguns sites nos últimos tempos é o "scroll infinito". Trata-se daquele comportamento da página de linha do tempo de perfis do Facebook entre outras redes sociais. Quando você se aproxima do fim da página, ela automaticamente carrega mais elementos e, consequentemente, ajusta o scroll da página, que pode ser rolada mais para baixo. Embora o scroll infinito gere uma experiência interessante para o usuário, também pode se tornar um vilão, quando mal planejado.

Neste artigo, veremos como montar uma página com scroll infinito, mas também discutiremos as preocupações a serem tomadas para garantir a acessibilidade do seu site.

Como implementar o scroll infinito

A forma mais acessível de se implementar um scroll infinito e que preserva as avaliações da página para os buscadores (SEO) é utilizando um conceito que já falamos aqui no blog: Hijax. Se você não leu o artigo ou não conhece o conceito, leia o Artigo sobre Hijax.

Uma forma de implementação é a seguinte:

  • Você implementa a página utilizando uma paginação convencional, que mostra links para a página anterior e para a próxima página.
  • Depois você acrescenta um código javascript que faça o seguinte:
    1. Esconde os links de paginação.
    2. Inclui um evento para ser disparado quando o scroll chega no final da página. Este evento fará o seguinte:
      1. Requisita os dados da página seguinte via Ajax.
      2. Obtém os elementos da página carregada e inclui em baixo dos itens exibidos.
      3. Atualiza o número da página atual (para saber que a próxima requisição ajax deverá carregar a página seguinte).

Porém, se a sua página possui um rodapé, talvez seja melhor não acionar o evento automaticamente. Afinal, se ele for acionado automaticamente ao chegar no final do scroll, então o usuário não vai conseguir ver e interagir com os links mostrados no rodapé, a não ser que carregue todas as páginas. Isso acontece atualmente com o Facebook. Experimente visitar a linha do tempo de algum amigo e tente clicar nos links do rodapé. Se sua conexão for rápida, você terá certa dificuldade.

Então se você tem um rodapé (especialmente um rodapé com links ou com informações importantes), talvez seja melhor o javascript substituir os links de paginação por um botão "Carregar mais". Ao clicar neste botão, você aciona o evento Ajax e atualiza a página incluindo os novos elementos entre os elementos exibidos e o botão "Carregar mais". Assim, o usuário conseguirá acessar o rodapé tranquilamente e só irá carregar mais elementos se ele quiser.

Exemplo de implementação de scroll infinito

No código abaixo, implementei um scroll infinito que utiliza o jQuery para facilitar a requisição Ajax e interação com a página. Há duas versões do script "scrollinfinito.js", sendo que um utiliza o botão "Carregar mais" e o outro é ativado quando o scroll atinge o final da página.

Arquivo index.php (com quatro páginas simples):

<?php
$pagina = isset($_GET['pagina']) ? $_GET['pagina'] : 1;
?>
<!DOCTYPE html>
<html>
  <head>
    <title>Exemplo</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
    <script type="text/javascript" src="scrollinfinito.js"></script>
  </head>
  <body>
    <header>
      <h1>Exemplo</h1>
    </header>
    <section>
      <article>
        <header>
          <h2>Elemento <?= ($pagina - 1) * 3 + 1 ?></h2>
        </header>
        <p>Conteúdo <?= ($pagina - 1) * 3 + 1 ?></p>
      </article>
      <article>
        <header>
          <h2>Elemento <?= ($pagina - 1) * 3 + 2 ?></h2>
        </header>
        <p>Conteúdo <?= ($pagina - 1) * 3 + 2 ?></p>
      </article>
      <article>
        <header>
          <h2>Elemento <?= ($pagina - 1) * 3 + 3 ?></h2>
        </header>
        <p>Conteúdo <?= ($pagina - 1) * 3 + 3 ?></p>
      </article>
    </section>
    <nav class="paginacao">
      <?php if ($pagina == 1): ?>
      <span>Página Anterior</span>
      <?php else: ?>
      <a rel="prev" class="anterior" data-pagina="<?= $pagina - 1?>" href="index.php?pagina=<?= $pagina - 1 ?>">Página Anterior</a>
      <?php endif ?>
      <?php if ($pagina < 4): ?>
      <a rel="next" class="proxima" data-pagina="<?= $pagina + 1 ?>" href="index.php?pagina=<?= $pagina + 1 ?>">Próxima Página</a>
      <?php else: ?>
      <span>Próxima Página</span>
      <?php endif ?>
    </nav>
    <footer>
      <p>Rodapé de exemplo</p>
    </footer>
  </body>
</html>

Arquivo scrollinfinito.js (com botão "Carregar mais"):

$(document).ready(function(){
    // Esconder paginacao
    var paginacao = $(".paginacao");
    paginacao.hide();

    // Encontrar o link para proxima pagina
    var proxima = paginacao.find(".proxima");

    // Se encontrou o link
    if (proxima.length > 0) {

        // Criar o botao "Carragar mais"
        var botao = $("<a id='carregar-mais' href='#'>Carregar mais</a>");
        botao.data("proxima-pagina", proxima.data("pagina"));

        // Criar evento ao clicar no botao
        botao.click(function(){

            // Carregar proxima pagina
            jQuery.ajax(
                "index.php",
                {
                    "data": {
                        "pagina": $(this).data("proxima-pagina")
                    },
                    "dataType": "xml",

                    // Funcao para atualizar elementos da pagina
                    "success": function(data, text_status, xhr) {

                        // Obter elementos e inclui-los na pagina
                        $("section").append(
                            $(data).find("section").html()
                        );

                        // Obter link para proxima pagina
                        var proxima = $(data).find(".paginacao .proxima");

                        // Se existe proxima pagina: atualizar status
                        if (proxima.length > 0) {
                            $("#carregar-mais").data("proxima-pagina", proxima.data("pagina"));

                        // Se nao existe proxima pagina: esconder botao "Carregar mais"
                        } else {
                            $("#carregar-mais").hide();
                        }
                    }
                }
            );
        });

        // Incluir o botao no lugar da paginacao
        botao.insertBefore(paginacao);
    }
});

Arquivo scrollinfinito.js (com ativação quando o scroll atinge final da página):

$(document).ready(function(){
    // Esconder paginacao
    var paginacao = $(".paginacao");
    paginacao.hide();

    // Encontrar o link para proxima pagina
    var proxima = paginacao.find(".proxima");

    // Se encontrou o link
    if (proxima.length > 0) {
        $("body").data("proxima-pagina", proxima.data("pagina"));

        $(window).on("load scroll", function(){

            // Se nao possui proxima pagina ou nao esta no final do scroll: abortar
            if (
                $("body").data("proxima-pagina") == 0 ||
                $(window).outerHeight() + $(window).scrollTop() < $(document).outerHeight()
            ) {
                return;
            }

            // Carregar proxima pagina
            jQuery.ajax(
                "index.php",
                {
                    "data": {
                        "pagina": $("body").data("proxima-pagina")
                    },
                    "dataType": "xml",

                    // Funcao para atualizar elementos da pagina
                    "success": function(data, text_status, xhr) {

                        // Obter elementos e inclui-los na pagina
                        $("section").append(
                            $(data).find("section").html()
                        );

                        // Obter link para proxima pagina
                        var proxima = $(data).find(".paginacao .proxima");

                        // Atualizar status
                        if (proxima.length > 0) {
                            $("body").data("proxima-pagina", proxima.data("pagina"));
                        } else {
                            $("body").data("proxima-pagina", 0);
                        }
                    }
                }
            );
        });

    }
});

Bom, este é apenas um exemplo. Para utilizá-lo na prática, você certamente precisará entender o código para fazer as devidas adaptações.

Experimente acessar a página com o javascript desabilitado. Sim, ela funciona normalmente com uma paginação convencional.

22 comentários

Se o mundo acabar! disse...

Rubens estou com problema com esse tutorial!!!
Primeiro queria dar os meus parabéns pelo excelente blog
e agora estou com problema com scroll infinito usando resultado de busca php

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá "Se o mundo acabar!",
Posso ajudar se você me passar mais detalhes sobre o problema:
Qual está sendo o problema exatamente?
Qual navegador está usando?
Você experimentou o código do post exatamente como ele está?
Note que para funcionar com outras paginações você precisa adaptar o código, pois ele captura o botão "próximo" através da class "proximo" e captura o número da página pelo data attribute (data-pagina). Também precisa adaptar a requisição em Ajax, que consulta os resultados da próxima página.
Enfim, este post não deve ser usado apenas copiando/colando código. É preciso entender o que cada linha faz pois o objetivo aqui foi passar a ideia de como fazer e mostrar um exemplo.

Se o mundo acabar! disse...

Estou usando chrome e dentro do "a" esta assim href=".$PHP_SELF."?pg=$i_pg2&$exe1 rel='next' class='proxima' data-pagina=' $pg + 1'
O arquivo scrollinfinito não alterei (estou usando "com ativação quando o scroll atinge final da página"
a busca esta programada para retornar 5 arquivos por pagina e não sai destes cinco primeiro

Sousa Varela disse...

Boas!

Copie e colei o código sem qualquer alteração e o efeito não funcionou para nenhuma das duas opções. Não sei se o tamanho do ecrã do monitor influencia nisso.
Abraços excelente artigo

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Sousa Varela
O tamanho do monitor influencia, pois assim que abre a página, ele já carrega a quantidade necessária de "páginas" para ocupar toda a tela. Mas não deve ser este o problema.
Você salvou os arquivos com os nomes iguais aos do artigo? Está usando um PHP recente? Está usando um navegador recente? Sem muitos detalhes do problema acho difícil ajudá-lo. Se preferir, me mande um e-mail: rubs33@gmail.com

Anônimo disse...

Onde eu coloco um código desses no blogger.E tem como personaliza-los tipo o botão "Carregaar Mais" da Capricho:
http://capricho.abril.com.br/

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
Este recurso pode ser feito no Blogger, embora eu recomende fortemente que, para fazer isso, você tenha certo conhecimento de programação (javascript) e familiaridade com o editor de código do Blogger.

Em todo caso, o procedimento é este: no editor de HTML do blogger, você vai precisar encontrar o trecho de código que monta os botões de avançar e voltar. Então você coloca algum "id" ou "class" nestes botões para identificá-los pelo JavaScript (no artigo, coloquei um elemento com class "paginacao" e, dentro dele, tem os botoes com class "proxima" e "anterior").

Depois, é só incluir o jQuery e incluir o código JavaScript que faz a mágica (esconde o bloco onde ficam os botões avançar e voltar e inclui um botão "carregar mais artigos" no lugar). Códigos JavaScript podem ser colocados em qualquer lugar do HTML, dentro da tag "script".

Quanto ao estilo deste botão, precisa conhecer sobre CSS. Você coloca um "class" no botão e coloca as "regras de estilo" pelo CSS.

Se não estiver familiarizado com edição de código, JavaScript ou CSS, me mande um e-mail que posso dar uma ajuda: rubs33@gmail.com.

Anônimo disse...

Você está querendo dizer que eu tenho que substituir os códigos dos botões "proxima" e anterior" pelo código que você mostou?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo
Na verdade o código que eu mostrei no artigo é apenas um exemplo. O objetivo era mostrar como fazer e não servir de "copie e use". Ele só funciona em um cenário muito específico, que possui estas características:
* o botão avançar/voltar são links para a mesma página, mas mudam o atributo "pagina", passado na URL via GET.
* a página de destino é compatível com XML, para o código ajax capturar determinado pedaço da página.
* o conteúdo que se quer carregar dinamicamente está em uma tag "section".
* a área onde ficam os botões de navegação tem a class "paginacao".
* os botões de avançar e voltar da navegação possuem a class "avancar" e "voltar" respectivamente.

Se uma destas características não estão presentes no seu cenário, o código simplesmente não vai funcionar. Ou seja, o código é meramente ilustrativo e certamente precisará de alguns ajustes para funcionar em outro cenário. Se não compreender o que o código faz, não conseguirá fazer as adaptações, por isso é importante alguma experiência com JavaScript para fazer este comportamento.

Mas, como eu disse, se quiser uma ajuda em como adaptar o código para funcionar no blogger, é só falar.

Rubens Takiguti Ribeiro (autor do blog) disse...

Aliás, sendo mais direto: não, você não deve substituir o código dos botões por um bloco javascript. O que eu disse é que o código javascript, assim que o usuário entrar na página e ele ser executado, vai esconder os botões e criar um novo botão dinamicamente. Ou seja, quem não tem javascript habilitado, vai continuar conseguindo usar o site, mas com os botões de paginação convencionais. Mas quem estiver com javascript habilitado, terá uma experiência diferente, pois bastará clicar no botão carregar mais para obter mais conteúdo, sem sair da página.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Zata

Na verdade entendo que o ideal é fazer ferramentas acessíveis, independente da complexidade. A forma como mostrei aqui no artigo explica como transformar uma página acessível (que já usa paginação) em uma página mais dinâmica, transformando elementos e comportamentos. Este é o princípio do hijax:
http://rubsphp.blogspot.com.br/2012/09/hijax-ajax-nao-obstrutivo.html

O artigo que você citou mostra o básico sobre como montar um carregamento dinâmico, mas parece não ter a preocupação com a acessibilidade, nem com o SEO.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Arouchaman

Você testou copiar o código exatamente como mostrado no artigo ou está tentando aplicar este comportamento em uma página sua?

Talvez o problema esteja na requisição Ajax. No exemplo, coloquei um callback na ocorrência de "success" do Ajax, mas não coloquei callback na ocorrência de "error". Você pode colocar um debug assim:

"error": function() { window.alert("erro de ajax"); }

arouchaman disse...

Copiei todo o código exatamente como estava no artigo. No caso, em qual parte de código entraria esse error reporting ? Estou testando o código no localhost, isso muda alguma coisa ?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Arouchaman

Não tem nenhum problema rodar em localhost. Eu rodei aqui em localhost numa boa. Criei uma pasta "teste" e coloquei o arquivo index.php e o scrollinfinito.js, depois chamei em http://localhost/teste/ e funcionou normal.

Note que coloquei dois códigos javascript diferentes, mas você deve usar apenas um deles (cada um tem um comportamento).

Esse debug que informei é pra ser colocado na chamada de ajax. Se não está familiarizado, sugiro que dê uma lida nesta documentação:
https://api.jquery.com/jQuery.ajax/

Unknown disse...

Opa, gostei muito, bem simples e fácil, porém estou com um problema, eu faço tipo um loop antes e eu queria mostrar 1 item por página, aqui vai como tá:




<--conteudo repeat-->



Aguardo respostas, vlw mesmo cara! :)

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Rosana
Infelizmente o blogger não permite utilizar tags nos comentários e o código da sua pergunta foi cortado. Se puder, poste seu código em algum site como pastebin.com, depois deixa o link aqui. Ou então me manda por e-mail rubs33@gmail.com