2009
Todos estes anos trabalhando com desenvolvimento web e eu nunca havia implementado um autocomplete. Após uma rápida busca o candidato óbvio foi o jQuery Autocomplete plugin. A implementação foi tranquila. O autocomplete em questão puxa uma lista de aeroportos de um arquivo JSON.
O problema da acentuação
Infelizmente o plugin é rígido ao tratar caracteres. O comportamente desejado era que os mesmos resultados fossem devolvidos digitando-se, por exemplo, “são” ou “sao” (São Paulo e outros resultados apareceriam neste caso). Isso não acontecia. Ao digitar sao, “São Paulo” não era um dos resultados. Entretanto, percebi que o plugin não diferenciava caixa alta e baixa: SÃO e são retornam o mesmo resultado.
A gambiarra
Modificar plugins é uma péssima prática por vários motivos:
- um bug pode ser introduzido (e só percebido depois)
- se uma nova versão do plugin for lançada, a modificação tem que ser refeita
- pior: a modificação pode não funcionar numa nova versão
Plugins bem feitos geralmente fornecem formas de personalização do comportamento através de objetos de configuração. Em algumas situações, como no nosso caso, isso não é possível. Vasculhei o código do plugin pelo método toLowerCase (conversão de caixa alta para caixa baixa):
177 178 179 180 | if( data[i].result.toLowerCase() == q.toLowerCase() ) { result = data[i]; break; } |
255 256 | if (!options.matchCase) currentValue = currentValue.toLowerCase(); |
Continuei buscando e encontrei ocorrências da conversão em mais cinco ou seis lugares. Então criei um método prototype para o objeto String chamado filterData:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | String.prototype.filterData = function() { var str = this.toLowerCase(), specialChars = [ {val:'a', let:'áàãâä'}, {val:'e', let:'éèêë'}, {val:'i', let:'íìîï'}, {val:'o', let:'óòõôö'}, {val:'u', let:'úùûü'}, {val:'c', let:'ç'}, ], regex; for (var i in specialChars) { regex = new RegExp('[' + specialChars[i].let + ']', 'g'); str = str.replace(regex, specialChars[i].val); regex = null; } return str; }; // Exemplo de uso alert('Isto é um teste. NÃO podemos ter acentos nem CAIXA ALTA!'.filterData()); |
Finalmente, para encerrar a gambiarra, substitui no código do plugin todas as ocorrências de toLowerCase para filterData:
177 178 179 180 | if( data[i].result.filterData() == q.filterData() ) { result = data[i]; break; } |
255 256 257 258 | if (!options.matchCase) currentValue = currentValue.filterData(); // e em todo o resto do plugin |
E funciona! Vou tentar entrar em contato com o autor do plugin sugerindo esta funcionalidade para a próxima versão.
Observação: utilize UTF-8
Para esta modificação funcionar a página HTML deve ter o seu charset declarado como UTF-8. O charset ISO-8859-1 não funciona. Sei disso porque a página original teve que ser alterada para UTF-8. Com ISO-8859-1 buscas com acentos não retornavam resultado algum. Mistério, não tenho idéia porque isso ocorre.
Dá pra trocar o seu for por:
$(specialChars).each(function(i){
regex = new RegExp(‘[' + this.let + ']‘, ‘g’);
str = str.replace(regex, this.val);
regex = null;
});
Oi, tem alguma coisa de errado na String.prototype.filterData , quando digito lim
ele me retorna lundefinedm, testei no chrome.
antes:lim
depois:lundefinedm
fiz um console.log antes do for e depois do for.
Editar o arquivo bsn.AutoSuggest_2.1.3.js procurar por encodeURIComponent() e remover apenas isto e irá funcionar a acentuação.
ficando assim:
var url = this.oP.script((this.sInp));
antes era:
//var url = this.oP.script(encodeURIComponent(this.sInp));
Olá.
Eu estava com problemas com acentuação em um sort de tabela. Com o filterData a coisa funcionou.
Valeu!!!