Dicas: jQuery performance

Duas coisas que todo o bom desenvolvedor deve se preocupar: segurança e performance.

Comecei a me preocupar e estudar mais a fundo performance em PHP quando passei a ter problemas com o crescimento do And After, para isso desenvolvi um sistema de cache inteligente utilizando o cache do Code Igniter, que aumentou muito a performance de entrega das páginas – e mesmo assim tem muito que pode melhorar.

Performance no front-end

Com a reduação dos custos computacionais (cloud, balance e servidores mais acessíveis) os problemas de processamento do backend muitas vezes são resolvidos com o "liga mais máquina", mas os problemas de performance no front-end são sentidos pelo usuário – e não temos controle sobre o computador do usuário.

A redução do custo do acesso a internet banda larga (internet 1Mb a R$35,00) permite que carreguemos mais código e imagens com mais qualidade sem prejudicar muito a entrega, mas ainda temos dois pontos principais de otimização de sites front-end que independem de banda ou do servidor:

  1. Renderização do código
  2. Performance do javascript

A forma e ordenação de como o HTML é escrito definem como ele será renderizado – e cada browser fará isso de uma forma, por isso é muito importante seguir as boas práticas e recomendações da W3C para estar preparado para o futuro, e não preso aos navegadores do passado.

Assim como o html, cada navegador tem a sua forma (ou engine) para lidar com javascript, o que diferencia muito a performance de algumas funções em um navegador ou outro – achar a forma que atende melhor seus usuários (sem ficar preso a navegadores velhacos) é o que eu vejo como melhor opção.

Se você quer entender um pouco mais sobre javascript recomendo a leitura dos slides sobre performance em javascript que postei anteriormente, os slides deste post tratam exclusivamente da biblioteca jQuery.

jQuery performance

Os slides abaixo apresentam diversos testes de performance com a biblioteca jQuery, em diversos navegadores. 

 

Uma boa forma de mensurar e testar seu código é criando uma rotina de testes de performance, que pode ser usando alguma biblioteca (ainda não usei para teste de performance) e o "modo macho", criando um cronômetro em javascript e marcando o tempo de execução de loops com cada função que você quer testar.

Manipulando os erros do jQuery Validate de forma diferente

Por padrão, quando se usa o plugin jQuery Validate (se você ainda valida seus formulários na mão, há grandes chances de você estar perdendo MUITO tempo), ao lado de cada campo é exibida a mensagem de erro adequada (campo obrigatório, formato inválido, etc…).

Mas, e se você quiser trabalhar de forma diferente? Vamos lá…

Exibindo todas as mensagens de erro em uma lista

$("form#meuform").validate({
errorLabelContainer: "#minhalista",
errorElement: "li"
});

Agora, e se você não quiser mostrar nenhuma mensagem de erro? Pode ser útil se você for marcar apenas os campos com uma borda vermelha, por exemplo. Simples, sobreescreva o método errorPlacement:

$("form#meuform").validate({
  errorPlacement: function(error,element) {
    return true;
  }
});

Você pode, ainda, querer mostrar uma mensagem única de erro. Ao invés de colocar uma mensagem por campo, apenas dizer "Seu formulário contém erros". Para isso, sobreescrevemos o mesmo método do exemplo anterior e escrevemos no elemento #minhalista a mensagem, e o deixamos visível. Ao mesmo tempo, sobreescrevemos unhighlight, verificando se a quantidade de erros for 0. Nesse caso, escondemos o elemento #minhalista para que a mensagem suma.

$("form#meuform").validate({
  unhighlight: function(element, errorClass) {
    if (this.numberOfInvalids() == 0) {
      $("#minhalista").hide();
    }
    $(element).removeClass(errorClass);
  },
  errorPlacement: function(error,element) {
    $("#minhalista").html("
Possui erros!").show();
    return true;
   }
});

 

 

As opções de tratamento das validações são gigantescas, explorem-nas!

Validação simples de formulário com jQuery

Normalmente opto pelo uso do jQuery Validate para validação de formulário (veja como ignorar o jQuery Validate), mas em uma tela que estava trabalhando eu tinha vários formulários que abriam em modal (usando o PrettyPhoto) e eram manipulados antes da abertura dependendo das ações do usuário naquela tela.

Ao invés de usar o validate achei que seria mais simples implementar uma função para validação, seguindo a seguinte regra:

  • Os elementos com a classe "required" devem ser obrigatórios
  • Adicionar a classe "error" aos elementos obrigatórios não preenchidos
  • Exibir um elemento com a mensagem de erro do formulário
  • Retornar true se passar na validação

A função ficou bastante simples e poderia ser escrita facilmente sem o uso do jQuery.

Validação sem plugin

function validateForm(form){
    var err = 0;
    form.children('.required').each(function(){
        if($(this).val() == '' || $(this).val() == null){
            $(this).addClass('error');
            form.children('.error_form').show();
            err++;
        }
    })
    if(err == 0)
        return true
}

 

Update

Seguindo a dica do Chris, a validação do exemplo acima pode ser substituída por uma expressão regular que verifica se a string é vazia no javascript, como no exemplo abaixo:

function notNull (x){
        if(a!="" && !(a.match(/^\s+$/))){
            return true;
        }else{
            return false;
        }
}

A função segue a seguinte lógica:

  1. Recebe como parâmetro um objeto do jQuery (optei por isso ao invés do id ou classe pois meu código já tinha uma variável com o form selecionado por questões de performance.
  2. Cria uma variável chamada err com valor 0, que irá contabilizar os erros (caso exitam).
  3. Percorre todos os elementos do formulário e busca pelos que tenham a classe "required", percorrendo todos estes e:

    1. Verifica se o valor do elemento é vazio ou null, se for adiciona a classe "error" no elemento e mostra o elemento com classe "error_form" que está no formulário (caso exista).
    2. Soma 1 a variável err.
  4. Se depois de percorrer todos os elementos com classe "required" nenhum erro for contabilizado retorna true.

 

Seria possível retornar também o número de erros em caso de false, modificando o final da função para:

    if(err == 0)
        return true
    else
        return err;

Sei que validação já é um assunto um pouco "manjado" de javascript e muita gente opta pelos plugins, resolvi compartilhar para mostrar como funciona e exeplificar que nem sempre é necessário a gente inserir plugin para tudo o que vamos fazer…

Seletor por classe no javascript

Vi alguns questionamentos em fóruns sobre seletor de classe em javascript e resolvi compartilhar uma solução do fórum, que extende o document para que seja possível fazer seleção de elementos por classe sem a necessidade de usar uma biblioteca (jQuery, por exemplo) para isso.

Já postei um exemplo em HTML5 da selector API (querySelector), mas como isso só é suportado por browsers modernos a solução abaixo quebra o galho (e se getElementsByClassName for nativa do navegador ele usa a função nativa).

Função getElementByClassName

A função abaixo permite que você user document.getElementByClassName('nomeDaClasse') para fazer seletores sem o uso de nenhuma biblioteca.

if (document.getElementsByClassName == undefined) {
    document.getElementsByClassName = function(className)
    {
        var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)");
        var allElements = document.getElementsByTagName("*");
        var results = [];

        var element;
        for (var i = 0; (element = allElements[i]) != null; i++) {
            var elementClass = element.className;
            if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
                results.push(element);
        }

        return results;
    }
}

 

Na maioria dos projetos uso jQuery, mas tem alguns que o javascript é tão simples que não é necessário os 29Kb – e esse seletor de classes já quebra o galho. 🙂

Solução de um colaborador do DevShed.

jQuery – width de elemento hidden

O jQuery em sua versão 1.4.3 (já leram sobre a jQuery 1.5 que acabou de sair do forno?) tem um bug que impossibilita o uso do .width em elementos que não estão v isíveis na tela (com o uso do display: none), o retorno sempre é 0 para a largura desses elementos.

Segundo o  bug report foi corrigido na versão 1.4.4 (em um dos projetos que estou trabalhando a versão do jQuery era a 1.4.4 e o bug estava presente – como o projeto já estava em fase final achei melhor não alterar a versão da biblioteca e utilizar uma correção quando necessário).

Para quem está em situação parecida, segue um pequeno hack que manipula o CSS do elemento, pega o width e depois desfaz a alteração do CSS.

Usando o .width em elementos invisíveis

            $(this).css({ position: 'absolute', display: 'block' });
            var elemWidth = $(this).width();
            $(this).css({ position: '', display: 'none' });

O que o hack faz é deixar o elemento com display block e position absolute (assim o jQuery não se perde na hora de calcular a largura do elemento), feito isso você pode usar o .width e voltar os valores do position e display do elemento para como estavam antes.

Já passaram por este problema? Como resolveram (ou resolveriam)?