jQuery.when() – executar função depois de vários carregamentos ajax

Estou desenvolvendo um projeto para um cliente da GS Solutions onde um template será montado dinâmicamente no front-end através do consumo de vários JSONS.

Em um caso específico eu preciso gerar uma lista (array) de jogos, que estão divididos em 3 arquivos JSON diferentes e uma rotina deve ser executada depois desta lista populada. 

Como fazer isso de forma assíncrona após o carregamento dos 3 arquivos por ajax?

jQuery.when()

A função when do jquery permite que você execute um callback após uma série de ações terem sidos realizadas, como o carregamento assíncrono de quantas URLs você precisar.

Exemplo

Quero carregar jogos-1.json e jogos-2.json, percorrer os elementos de cada uma delas e adicionar em um array. Feito isso, quero percorrer este array e imprimir os resultados.

var jogos = [];
$.when(
	$.getJSON('jogos-1.json'),
	$.getJSON('jogos-2.json')
).done(function(jogos1,jogos2){
	/* Será executado somente após as 2 requisições ajax serem carregadas
	Aqui você poderia manipular os dados (jogos1 e jogos2),
	fazer merge dos objetos, ordenar, etc. */
	for (var jogo in jogos1){ jogos.push(jogo) }
	for (var jogo in jogos2){ jogos.push(jogo) }
});

Um exemplo bem simples mas que exemplifica uma forma eficiente de fazer o carregamento assíncrono mesmo quando nós precisamos de todos os request completos antes de executar o callback.

Você pode adicionar quantos eventos forem necessários como parâmetros na função when e deve adicionar o mesmo número de parâmetros na função dentro do done, cada parâmetro representa um resultado do when.

Tem mais dicas? Escreva nos comentários!

Usando resolve e reject da Deferred() da jQuery

O objeto Deferred da jQuery é extremamente poderoso e ainda não muito utilizado. Uma coisa interessante nele é registrar cadeias de chamadas (callbacks) e poder associá-las a estados de sucesso (resolve) ou erro (reject) de funções, sejam elas síncronas ou assíncronas.

Para o exemplo, preferi usar chamadas síncronas, visando a simplicidade. A idéia é que o usuário clique na área verde (uma div de id green) de uma página (o que vamos tratar como sucesso). Se clicar na vermelha (uma div de id red), trataremos como erro.

No javascript, vamos então criar um objeto Deferred:

var def = $.Deferred();

Vamos então a esse objeto associar alguns handlers:

– quando ele for resolvido:

def.done(function() {
   alert("Você clicou na área verde :-)");
});

– quando for rejeitado:

def.fail(function() {
   alert("Você clicou na área vermelha :-(");
});

– quando for tanto rejeitado como resolvido

def.always(function() {
   alert("Obrigado por testar!");
});

Com isso, temos 2 cadeias definidas já. Em caso do objeto ser resolvido, será dado um alert que indica que usuário clicou na área verde e depois um outro agradecendo o teste. Em caso de ser rejeitado, um alert falando que clicou na área vermelha e aí a mesma mensagem de agradecimento é invocada.

Para resolver ou rejeitar, é simples:

$("div#green").on("click", def.resolve);
$("div#red").on("click", def.reject);

Vejam no jsFiddle esse exemplo: http://jsfiddle.net/3J5zA/2/

Existem usos mais complexos e interessantes para o objeto Deferred, o exemplo feito é para mostrar como criar cadeias de resolve e reject de forma simples e invocá-las. Na documentação oficial pode-se encontrar mais explicações sobre os métodos usados, bem como de outros.

Lazy Load – Documentação e demo

Nas últimas semanas participei do Front in Curitiba e esta semana participei da trilha de front-end do The Developers Conference, os eventos e conversas de corredor deram uma animada para eu continuar  brincando com código e terminar o Pong em HTML5, que quero integrar com NodeJS para tornar multiplayer.

Mas antes disso resolvi melhorar o repositório do jQuery Lazy Load, criar uma mini-documentação (que é tudo que o plugin exige) e uma página de demo.

Na documentação exemplifiquei como deixar um fallback para usuários com javascript desabilitado, o plugin ainda não teve nenhuma atualização de código, mas os próximos passos são:

  • Parametrizar as configurações
  • Documentar e exemplificar o uso de imagens de loading
  • Criar a página em inglês da documentação e demo

Espero que a documentação seja útil! 🙂

Nova versão do jQuery Lazy Load

Conversando com o Rafael Cavalcante sexta ele me lembrou que eu ainda devia melhorias para aplicar no jQuery Lazy Load Plugin.

Comecei sexta e testei hoje, está tudo no Github.

jQuery Lazy Load Changelog

  • Melhoria de performance
  • Não precisa mais de classe para trabalhar

Changelog detalhado:

Devido as preocupações com performance de javascript mudei um pouco forma como o plugin trabalha.

Com o uso do grep no lugar do each, o jQuery Lazy Load está agora removendo do seu array  todas as imagens que já for carregadas. Elas não são percorridas repetidamente (como acontecia na versão 0.5) e a performance melhorou bastante por conta disso.

Também por conta desta nova forma de trabalhar com o array não é mais necessário o uso de classe (a classe era utilizada para verificar se uma imagem já havia sido carregada). Como preciso manipular menos o DOM também houve ganho de performance por conta disso.

 

Como usar o jQuery Lazy Load

Baixa a última versão do plugin no Github.

Chamando os scripts

Chame o jQuery e o jQuery Lazy Load plugin na sua página.

Preparando as imagens

As imagens que utilizarão o lazy load devem ter a tag data-src com o valor da url da imagem que deverá ser carregada no conteúdo.

Uma dica é chamar o src das imagens para uma imagem transparente e sempre estipular a largura e altura da imagem no próprio HTML, assim sua página vai renderizar mais rápido pois você irá reduzir o reflow do browser.

Outra dica é através de CSS colocar uma imagem de loader como background das imagens que utilizarão o plugin de lazy load, assim se as imagens demorarem para carregar seu usuário terá uma experiência melhor e saberá que algo está acontecendo.

 

Iniciando o Lazy Load

Por último, é só iniciar o plugin com o seletor das imagens que irão utilizar o plugin:

$(function() {
$('.lazy').lazyLoad();
});

 

Pronto, agora seus usuários só carregarão imagens quando necessário.

Lazy Load com jQuery [update]

Veja a página oficial do Lazy Load.

[changelog]

Saiu a nova versão do Lazy Load, veja o changelog completo da nova versão.

Visite o Github do projeto.

v1.0

  • Funciona sem classes
  • Otimização de performance – imagens já carregadas são arrancadas do array e não são mais percorridas

v0.5

  • Só funciona com seletor de classes nas imagens

Apliquei uma melhoria de performance no plugin (para não varrer repetidamente os elementos), ainda não consegui subir no Fiddle. Farei isso este final de semana e também vou documentar melhor o que ele faz para que mais pessoas possam contribuir com melhorias. 🙂

[/changelog]

 

Esta semana estava trabalhando em otimizações para a recém lançada home do iG e resolvemos aplicar o lazy load para melhorar ainda mais a experiência do usuário.

 

O que é Lazy Load?

Lazy load (ou "carregamento preguiçoso") é uma técnica de otimização de front-end que torna o carregamento inicial de uma página mais veloz inibindo o carregamento de imagens que não estão visíveis para o usuário.

Em páginas com uma altura grande e com uma quantidade alta de imagens razoável a técnica torna o carregamento muito mais veloz, e a medida que o usuário desce o scroll da página as imagens são carregadas para serem exibidas conforme a demanda.

A experiência do usuário é melhor pois a página é renderizada de forma mais rápidas, a fila de requisições é cortada portanto CSS e javascript são carregados e executados com menos tempo, e como acabei de ler As Leis da Simplicidade posso afirmar: reduzir tempo é um dos "processos de simplicidade" com o qual nós desenvolvedores devemos nos preocupar muito!

Mais um plugin de Lazy Load?

Tive a oportunidade de participar de todos os processos de desenvolvimento da nova home do iG e como estou pilhado em otimização nos últimos meses gostei muito da oportunidade de implementar otimizações após o lançamento. Dentre as otimizações aplicadas até agora uma das mais significativas no lado do usuário foi o Lazy Load.

Ps: até onde pesquisei o iG é o primeiro portal brasileiro a implementar a técnica 😀

Busquei diversas alternativas prontas e fiz vários testes, mas conclui que é impossível executar com sucesso o lazy load se o html for escrito com o link da imagem no atributo src.

os navegadores modernos (Chrome e Firefox, que consegui testar perfeitamente isso) na leitura inicial do DOM já identificam os links das imagens e adicionam na fila de requisições. Os plugins davam o efeito visual e até faziam uma nova requisição da imagem, sem nenhum efeito positivo para performance.

 

Plugin jQuery Lazy Load

A solução para realmente não carregar as imagens é não colocar o source da imagem no atributo src, para isso utilize uma imagem já carregada uma imagem transparente.

Para acessar a versão mais atualizada do plugin acess este fiddle.

jQuery Lazy Load

Adicione o plugin a sua página, necessita de jQuery (testado com v1.4.4)

/* lazyLoad */
(function($){
$.fn.lazyLoad = function() {
   var images = this,
       classe = this.selector.replace('.','');
   showVisible();
   $(window).scroll(function(){ showVisible(); })
   function showVisible(){
      images.each(function(){
         var img = $(this);
         if(img.hasClass(classe)){
            var imgTop = img.offset().top, wTop = $(window).scrollTop() + $(window).height() + 100;
            if(wTop > imgTop){ 
               img.removeClass(classe).attr('src', img.data('src')).fadeIn();
            } 
         }
      })
   }
};
})( jQuery );

Iniciando o plugin

Como a regra dos bons plugins manda, a inicialização do plugin é extremamente simples e o seletor deve ser uma classe que deve ser aplicada em todas as imagens que irão utilizar a técnica:

$('.lazy-load').lazyLoad();

 

O Html

O plugin utiliza o atributo data do HTML 5, portanto o HTML de todas as imagens que utilizam devem ficar da seguinte forma:

< img data-src="url-real-da-imagem" height="100" src="url-de-imagem-de-1px-transparente" width="100" / >

 

Testado em:

  • IE 7, 8, 9
  • Firefox
  • Chrome
  • Safari