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
- Confirme quantas cópias do jQuery a página carrega.
- Liste os ouvintes de evento do campo problemático.
- Pare a execução quando algo alterar o campo.
- 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.
getEventListenersno 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?
Referência: http://codigofonte.uol.com.br/artigos/8-plugins-de-jquery-para-incrementar-seu-formulario