Tableless - Desenvolvimento inteligente com Padrões Web

19/04/2011
Javascript

Conteúdo sob demanda com jQuery

Veja como exibir o conteúdo do seu site acabando com a necessidade de paginação e atualização da página.

Por


Nem sempre uma paginação é a melhor maneira de limitar o conteúdo exibido em um site. Às vezes pode ser bem chato ficar indo de página em página procurando alguma coisa, você acaba se perdendo. O Twitter é um bom exemplo. Na interface do aplicativo, o botão “load more” faz as vezes de uma paginação e carrega a próxima sequência de tweets em sua timeline.

Neste artigo, além do link para carregar os próximos conteúdos, veremos também uma abordagem mais dinâmica onde o conteúdo é carregado assim que o scroll atinge o final da página.

E por falar em Twitter, nossos exemplos utilizarão sua API para carregar os dados de exemplo, mas é claro que eles funcionam com qualquer script que forneça os dados em JSON.

Carregando tweets

A API do Twitter oferece a possibilidade de baixar os últimos tweets de um usuário utilizando apenas JavaScript. A API oferece opção de saída em vários formatos, entre eles XML, Atom e JSON (formato utilizado em nosso exemplo).

Na nossa chamada ajax passamos ainda o nome da conta no Twitter e o número da página dos tweets.

1
2
3
4
5
6
7
var usuario = 'tableless';
var formato = 'json';
var url = 'http://api.twitter.com/1/statuses/user_timeline.'+formato+"?callback=?";

$.getJSON(url, {screen_name:usuario, page:pagina}, function(tweets){
  // nosso código
});

O retorno, o objeto com os tweets:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
   {
      "favorited":false,
      "text":"http:\/\/bit.ly\/esqJUw Trabalha com TI?\u00a0N\u00e3o importa seu campo de atua\u00e7\u00e3o, confira essa oportunidade da Intel\u00a0 #ad",
      "retweet_count":0,
      "in_reply_to_screen_name":null,
      "in_reply_to_status_id_str":null,
      "place":null,
      "contributors":null,
      "retweeted":false,
      "in_reply_to_user_id":null
     ...
  }
]

Load more

Vamos então ao nosso primeiro exemplo, o botão para carregar mais tweets. Ao acessar a página são carregados automaticamente 12 tweets e os novos tweets só são carregados quando o usuário clica no link.

O HTML, que também será utilizado em nosso segundo exemplo, é esse:

1
2
3
<ul id="lista-tweets"></ul>

<a href="#" id="carrega-tweets">Mais!</a>

O primeiro passo é associar a função para exibir mais tweets ao elemento “#carrega-tweets”, impedindo também o funcionamento padrão do clique (que nesse caso seria ir para o link “#”) com o método preventDefault.

1
2
3
4
$('#carrega-tweets').click(function(e){
  retorna_tweets($(this).data('pagina'));
  e.preventDefault();
});

Ao clicar no link, nosso site executa a funcão retorna_tweets, recebendo como parâmetro a página dos tweets a serem carregados. A página fica armazenada no próprio elemento e é incrementada toda vez que ocorre um clique (o método data merece um outro artigo, por isso não vou falar muito sobre ele aqui).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function retorna_tweets(pagina){
  $('#carrega-tweets').hide();
  $('#lista-tweets').append('<li class="carregando">Aguarde, carregando...</li>');
  var screen_name = 'tableless';
  var formato = 'json';
  var url = 'http://api.twitter.com/1/statuses/user_timeline.'+formato+"?callback=?";

  $.getJSON(url, {screen_name:screen_name, page:pagina}, function(tweets){
    $('.carregando').fadeOut(function(){
      for(x in tweets)
        $('#lista-tweets').append('<li>'+tweets[x].text+'</li>');
      $(this).remove();
      $('#carrega-tweets').data('pagina', pagina + 1).fadeIn();
    });
  });
}

A função retorna_tweets segue o seguinte fluxo:

  1. Oculta o link de carregamento para evitar que a ação seja executada mais de uma vez ao mesmo tempo;
    1
    $('#carrega-tweets').hide();
  2. Adiciona um ítem à lista de tweets para informar ao usuário que o conteúdo está sendo carregado;
    1
    $('#lista-tweets').append('<li class="carregando">Aguarde, carregando...</li>');
  3. Através do método getJSON, recebe a lista de tweets da conta informada (no nosso caso, a conta do Tableless);
    1
    $.getJSON(url, {screen_name:screen_name, page:pagina}, function(tweets){
  4. Quando a API termina de enviar os dados pra gente, escondemos e removemos o loader e ao mesmo tempo adicionamos ítems à lista com o texto de cada tweet;
    1
    2
    3
    4
    5
    $('.carregando').fadeOut(function(){
      for(x in tweets)
        $('#lista-tweets').append('<li>'+tweets[x].text+'</li>');
        $(this).remove();
    });
  5. E, finalmente, incrementamos o número da página atual no link e voltamos a exibí-lo.
    1
    $('#carrega-tweets').data('pagina', pagina + 1).fadeIn();

Infinite Scrolling

Outra maneira interessante é carregar o conteúdo quando o scroll atinge o final da página. Essa abordagem é um pouco mais agressiva já que o usuário não tem total controle sobre o conteúdo que deseja ver. Por outro lado, ele não precisa se preocupar em clicar em nada para exibir mais conteúdo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var ajax = "";
$(function(){
  retorna_tweets(1);
  $('body').data('pagina', 1);
  inicializa_scroll();
});

function inicializa_scroll(){
  $(window).scroll(function() {
    if(($(window).scrollTop() + $(window).height() + 20) >= $(document).height()) {
      $(window).unbind('scroll');
      ajax.abort();
      retorna_tweets($('body').data('pagina'));
    }
  });
}

Nesse caso, como não possuímos um link, armazenamos o número da página a ser carregada no elemento body e iniciamos, é claro, o carregamento com a página 1. A função inicializa_scroll é responsável por associar o evento scroll da janela com nosso carregamento de tweets.

Dois detalhes importantes de usabilidade: quando o carregamento é executado nós removemos a associação do método scroll da janela para evitar duplicidades ($(window).unbind(‘scroll’)) e, além disso, interrompemos alguma chamada anterior (ajax.abort();). A variável global ajax foi criada no início do nosso javascript e ela vai receber a chamada ajax na função retorna_tweets.

Vamos analisar agora a comparação do scroll com a altura do documento:

1
$(window).scrollTop() + $(window).height() + 20) >= $(document).height()

O método scrollTop retorna a altura da parte oculta da página, que está acima da parte atual, acima do scroll. Já a chamada $(window).height() retorna a altura do viewport, da parte visível da página. A soma dos dois, quando o scroll está no final da página, resulta em $(document).height(), que é a altura total do documento. Alguns navegadores apresentam diferenças mínimas nesses valores, entre 1 e 5px. Por isso o adicionamos 20 pixels para uma “margem de erro”.

Pra finalizar, o método retorna_tweets sofreu algumas alterações com relação ao nosso primeiro exemplo. A primeira é a incrementação do número da página nos dados do elemento body e não em um link. Também armazenamos nossa chamada getJSON na variável ajax, conforme citado anteriormente. E, ao invés de exibir novamente o link, associamos novamente a função retorna_tweets ao scroll da janela através do método inicializa_scroll.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function retorna_tweets(pagina){
  $('#lista-tweets').append('<li class="carregando">Aguarde, carregando...</li>');

  var screen_name = 'tableless';
  var url = 'http://api.twitter.com/1/statuses/user_timeline.json?callback=?';

  $('body').data('pagina', pagina + 1);

  ajax = $.getJSON(url, {screen_name:screen_name, page:pagina}, function(tweets){
    $('.carregando').fadeOut(function(){
      for(x in tweets)
        $('#lista-tweets').append('<li>'+tweets[x].text+'</li>');
      inicializa_scroll();
      $(this).remove();
    });
  });
}

Clique aqui para fazer o download do código completo ou aqui para visualizar os exemplos no navegador

Por Davi Ferreira

Programador apaixonado pelo que faz. Responsável pelo desenvolvimento de sistemas e interfaces para Globo.com, Confederação Brasileira de Vôlei, Ricoh Brasil, Embratel entre outros.

http://www.daviferreira.com/blog

Veja os outros posts de

  • http://about.me/thiago.caiubi Thiago Caiubi

    Oi Davi, bacana o post!

    Na minha opinião o “Load more” parece mais legal por ser menos intrusivo ao usuário e o evento é executado apenas quando necessário.

    Gostaria de levantar alguns detalhes do “Infinite scrolling”:

    Em Janeiro o John Resig escreveu um post (http://ejohn.org/blog/learning-from-twitter/) sobre alguns problemas no Twitter e ressaltou alguns pontos na “escuta” de eventos como o scroll da window. Ele destaca o seguinte: “It’s a very, very, bad idea to attach handlers to the window scroll event.”

    Se não me engano a o Facebook carrega mais posts do usuário na primeira página do “Wall” e apenas no quando o primeiro scroll atinge o final do documento. Logo, pode ser uma implementação bem parecida com a sua, fazendo o unbind do evento.

    Por outro lado, ele também destaca: “Always cache the selector queries that you’re re-using”. Portando, seria legal ter um cache da consulta do window e document ao invés de executar $() a cada verificação se atingido final do documento ou não!

    Um abraço!

  • Gilson

    Olá Davi!
    Qual a diferença de usar o ‘e.preventDefault()’ ao invés de um ‘break’?
    Abração!

  • Gilson

    Outra!
    Qual a diferença de usar o ‘e.preventDefault()’ ao invés de um ‘return false’?
    Abração!

  • http://www.buscasaopaulo.com marcela

    É isso que estava procurando, se for o que estou pensando o google iamgens usa isso, vou testar

  • http://blog.focusteam.com.br Alex Figueiredo

    @Gilson

    Aqui tem uma explicação legal sobre a diferença de usar ‘preventDefault()’ ao invés de ‘break’ ou ‘return false’

    Abraço!

  • http://blog.focusteam.com.br Alex Figueiredo
  • Deivid Marques

    Mew sempre quis fazer um desses, vlw mesmo!! Ta bem mastigado vlw

  • http://www.acoimbra.com.br Anderson Coimbra

    Eu consigo carregar registros da minha tabela do Banco de dados? É só trocar o endereço da variável “url”? Ou precisa outra alteração?

    Abraço!

  • http://www.daviferreira.com/blog Davi Ferreira

    @Anderson: isso mesmo, é só trocar a url e adaptar o script que recebe os dados. Lembrando que sua URL deve retornar os dados no formato JSON!

  • Mauricio

    Bem, comecei recentemente a ver um pouco sobre ajax, json e outras coisas mais, então não to conseguindo entender muito bem como funcionam as coisas.
    Minha duvida é como que faço pra que esse sisteminha consiga retornar informações de uma database que eu criei?
    E onde nesse script mostra quais sao as variaveis que retornam um campo em especifico?

  • Giovani_araujo

    nossa perfeito esse tut…
    vlw

  • Ricardo Carmo

    Mauricio, o exemplo acima carrega informações do Twitter. Neste caso, a consulta ajax retorna um objeto serializado com vários atributos, dentre eles, um chamado “text”. O que o código faz é pegar o que veio em text (que é código HTML) e adicionar na lista “lista_tweets”. O foco do artigo é sobretudo no lado cliente da aplicação, não no lado do servidor, onde você deve tratar a requisição ajax e retornar o objeto serializado (em JSON, XML ou outro formato). Respondendo a sua segunda dúvida, “as variáveis que retornam um campo específico”, no exemplo acima é o objeto retornado, um dicionário com vários atributos – favorited, text, retweet_count, etc. Isso é definido no lado do servidor, ou seja, o próprio Tweeter ao responder a requisição, montou o dicionário desse jeito, porque foi programado para responder assim.
    Eu recomendo pesquisar sobre a sua linguagem preferida + Ajax. Por exemplo, pesquise sobre PHP + Ajax, Python + Ajax, etc. Procure por exemplos que mostrem o código nos dois lados, cliente e servidor.