Programação Por Equipe DevBras

Capa do artigo: Plugins jQuery brigando no mesmo campo: como achar o culpado e deixar um dono só

Plugins jQuery brigando no mesmo campo: como achar o culpado e deixar um dono só

Descubra qual script mexe em cada campo com as ferramentas do DevTools, elimine o jQuery carregado duas vezes e defina um único responsável por campo, de preferência o próprio HTML.

No formulário de entrega da Casa Pinho, o campo de data abre dois calendários ao mesmo tempo. O telefone perde o último dígito quando a validação mostra um erro. E, de vez em quando, o botão de enviar valida duas vezes com mensagens diferentes. Ninguém mexeu no código esta semana. A resposta curta: há mais de um script tratando o mesmo campo. Descubra quem está ligado a cada elemento, garanta que só existe um jQuery na página e defina um único dono por comportamento, começando pelo que o HTML já faz sozinho.

O problema raramente é um plugin ruim. É a soma: cada um foi escrito supondo que era o único a escutar aquele campo.

Por que plugins somados viram bug aleatório

Um plugin de máscara escuta a digitação e reescreve o valor do campo. Um plugin de validação escuta os mesmos eventos e lê o valor para decidir se mostra erro. Se a validação roda entre a tecla e a reescrita, lê um valor pela metade. Se ela redesenha o campo ao mostrar o erro, a máscara perde a posição do cursor. A ordem de execução depende de quem foi ligado primeiro, e isso depende da ordem dos scripts na página.

Há ainda os conflitos com o navegador. Um campo type="date" já tem calendário nativo; acrescentar um datepicker de plugin dá dois calendários. O preenchimento automático do navegador briga com um plugin de autocompletar que abre a própria lista. O comportamento parece aleatório porque depende de tempo, ordem e navegador, e cada pessoa da equipe reproduz um pedaço diferente.

Como descobrir quem mexe em cada campo

  1. Confirme quantas cópias do jQuery a página carrega.
  2. Liste os ouvintes de evento do campo problemático.
  3. Pare a execução quando algo alterar o campo.
  4. Desligue metade das inicializações e repita até isolar o par em conflito.

Uma cópia só do jQuery

No console do navegador, com a página aberta:

document.querySelectorAll('script[src*="jquery"]').forEach((s) => console.log(s.src));
console.log(window.jQuery && window.jQuery.fn.jquery);

A primeira linha lista os arquivos com "jquery" no nome; a segunda mostra a versão que está valendo agora. Se aparecem dois arquivos do núcleo, por exemplo um do CDN e outro local, você achou um culpado clássico. O segundo jQuery substitui o primeiro no objeto global, e os plugins que se registraram no primeiro somem. O sintoma típico é $(...).datepicker is not a function numa página onde o plugin claramente foi carregado.

Quem está escutando o campo

No Chrome, o console do DevTools tem a função getEventListeners, que lista todos os ouvintes ligados a um elemento, de qualquer biblioteca:

getEventListeners(document.querySelector('#telefone'));

Se aparecem três ouvintes de input e dois de keyup, há mais de um dono. Para ver só os que o jQuery registrou, existe $._data(elemento, 'events'), que é interno e não documentado, mas útil para diagnóstico. Lembre que eventos delegados ficam no ancestral, geralmente em document, e não no campo.

Na aba Elements, clique com o botão direito no campo e escolha a opção de parar em modificação de atributo (Break on, attribute modifications). Quando algum script trocar a classe, o atributo ou o próprio elemento, a execução para e a pilha mostra qual arquivo fez isso.

Bisseção das inicializações

Comente metade das chamadas de inicialização de plugin e teste. Se o bug sumiu, o culpado está na metade comentada; se não, na outra. Em quatro ou cinco rodadas você chega ao par em conflito, mesmo com dez plugins na página.

Como deixar um dono por campo

Depois do diagnóstico, escreva para cada campo quem é responsável por cada comportamento. Na Casa Pinho, a lista ficou assim: a data é do navegador, o CEP e o e-mail são do HTML, o telefone tem uma máscara mínima e a validação final é do servidor. Os detalhes de cada tipo de campo estão no guia de inputs HTML5 que ajudam.

<form id="form-entrega" method="post" action="/entrega">
  <label for="data-entrega">Data de entrega</label>
  <input type="date" id="data-entrega" name="data_entrega" min="2026-10-20" required>

  <label for="cep">CEP</label>
  <input type="text" id="cep" name="cep" inputmode="numeric"
         autocomplete="postal-code" pattern="[0-9]{5}-?[0-9]{3}" required>

  <label for="telefone">Celular com DDD</label>
  <input type="tel" id="telefone" name="telefone" autocomplete="tel" required>

  <label for="email">E-mail</label>
  <input type="email" id="email" name="email" autocomplete="email" required>

  <button type="submit">Confirmar entrega</button>
</form>

Para o telefone, se a máscara for mesmo necessária, uma só, ligada com um namespace de evento. O namespace permite desligar exatamente esse comportamento sem afetar os outros:

$(function () {
  const $tel = $('#telefone');

  $tel.off('.mascaraTel').on('input.mascaraTel', function () {
    const d = this.value.replace(/[^0-9]/g, '').slice(0, 11);
    let v = d;
    if (d.length > 2) v = '(' + d.slice(0, 2) + ') ' + d.slice(2);
    if (d.length > 7) v = '(' + d.slice(0, 2) + ') ' + d.slice(2, 7) + '-' + d.slice(7);
    this.value = v;
  });
});

O off('.mascaraTel') antes do on impede que a máscara seja ligada duas vezes se o trecho rodar de novo, por exemplo depois de recarregar parte da página por AJAX. A máscara é conforto: o servidor continua aceitando o telefone com ou sem formatação, como explica o guia sobre campo numérico em que a máscara não é a validação. E a regra que vale é repetida no servidor, como em validação jQuery repetida no PHP.

O que remover primeiro

Tire primeiro o que duplica o navegador: datepicker em campo de data, plugin de "required" em campo que já tem o atributo, plugin de placeholder. Depois, o que você não sabe atualizar: um plugin sem manutenção é um conflito esperando a próxima versão do jQuery. Por último, avalie se o jQuery ainda é necessário. Se sobrou uma máscara e um envio por AJAX, os dois cabem em JavaScript puro.

Erros comuns

  • Carregar o jQuery do CDN e do servidor: o segundo apaga os plugins registrados no primeiro e surgem erros de "não é uma função".
  • Datepicker de plugin em type="date": dois calendários na mesma tela, e cada um formata a data de um jeito.
  • Máscara e validação reagindo à mesma tecla: a validação lê o valor pela metade e mostra erro em dado correto.
  • Inicializar o plugin de novo a cada carregamento por AJAX: os ouvintes se acumulam e cada tecla dispara o código várias vezes.
  • Corrigir o sintoma com setTimeout: o atraso esconde a disputa num computador e ela reaparece num celular mais lento.
  • Acrescentar um plugin para consertar outro: a soma cresce e o próximo diagnóstico fica mais difícil.

Checklist

  • A página carrega exatamente um arquivo do núcleo do jQuery.
  • Cada campo tem um único responsável por máscara, um por calendário e um por validação na tela.
  • getEventListeners no campo mostra só os ouvintes esperados.
  • Nenhum plugin repete o que o HTML já faz no campo.
  • Comportamentos ligados por script usam namespace e não se duplicam ao rodar de novo.
  • O formulário envia e é validado no servidor com o JavaScript desligado.
  • Todo plugin restante tem versão anotada e alguém que sabe atualizá-lo.

Rode getEventListeners no campo mais problemático do seu formulário: quantos ouvintes de input aparecem, e você sabe de onde vem cada um?

Avalie este artigo

Referência: http://codigofonte.uol.com.br/artigos/8-plugins-de-jquery-para-incrementar-seu-formulario