Este artículo presenta una solución encapsulada para autocompletado en JavaScript, compatible con Internet Explorer 6 a 9. La implementación elimina la necesidad de archivos CSS adicionales y minimiza el código HTML requerido. Solo se necesita un archivo JavaScript, y se controla el envío de formularios al presionar Enter.
Para usarlo, se invoca la función inicializar con los parámetros: el identificador del campo de texto, la URL para las solicitudes AJAX y una función de retorno opcional que se ejecuta al seleccionar una sguerencia.
Código del archivo autocompletado.js
// Configuración inicial del campo de texto con eventos y parámetros AJAX
var contenedorSugerencias;
var funcionCallback;
var identificadorCampo;
function inicializar(identificador, urlServidor, funcionOpcional) {
identificadorCampo = identificador;
if (!urlServidor) return;
var urlEndpoint = urlServidor;
if (funcionOpcional) funcionCallback = funcionOpcional;
var campoTexto = document.getElementById(identificador);
campoTexto.setAttribute('autocomplete', 'off');
var agregarEvento = function(elemento, tipo, manejador) {
if (elemento.attachEvent) {
elemento.attachEvent('on' + tipo, manejador);
} else {
elemento.addEventListener(tipo, manejador, false);
}
};
agregarEvento(campoTexto, 'keyup', procesarTecla);
agregarEvento(campoTexto, 'keypress', bloquearEnvio);
}
function bloquearEnvio(evento) {
var evt = evento || window.event;
if (evt.keyCode === 13) {
if (evt.preventDefault) {
evt.preventDefault();
} else {
evt.returnValue = false;
}
}
}
function procesarTecla(evento) {
var evt = evento || window.event;
var codigoTecla = evt.keyCode;
var campoActual = evt.target || evt.srcElement;
if (codigoTecla === 40) {
navegarSiguiente();
} else if (codigoTecla === 38) {
navegarAnterior();
} else if (codigoTecla === 13) {
ejecutarCallback();
ocultarContenedor();
return false;
} else {
var valorActual = campoActual.value;
if (valorActual.length === 0 && contenedorSugerencias) {
ocultarContenedor();
} else {
solicitarDatos(valorActual, campoActual);
}
}
}
function obtenerTransporte() {
var transportes = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
];
for (var i = 0; i < transportes.length; i++) {
try {
var transport = transportes[i]();
if (transport) return transport;
} catch (ex) {}
}
return null;
}
function solicitarDatos(valor, campoRef) {
var xhr = obtenerTransporte();
if (!xhr) return;
xhr.open('POST', urlEndpoint, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
mostrarSugerencias(campoRef, xhr.responseText);
}
};
xhr.send(valor);
}
function calcularPosicion(objeto, propiedad) {
var valor = 0;
while (objeto) {
valor += objeto[propiedad];
objeto = objeto.offsetParent;
}
return valor;
}
function mostrarSugerencias(campo, datos) {
var elementos = datos.split(',');
if (!contenedorSugerencias) {
contenedorSugerencias = document.createElement('div');
contenedorSugerencias.id = 'contenedorAutocompletado';
contenedorSugerencias.style.cssText = 'position:absolute;display:none;border:1px solid #999;background:#fff;z-index:1000;';
document.body.appendChild(contenedorSugerencias);
var manejarClicExterno = function(e) {
var objetivo = e.target || e.srcElement;
if (!objetivo.id.startsWith('opcion_') && objetivo.id !== identificadorCampo) {
ocultarContenedor();
}
};
if (document.attachEvent) {
document.attachEvent('onmousedown', manejarClicExterno);
} else {
document.addEventListener('mousedown', manejarClicExterno, false);
}
}
var top = calcularPosicion(campo, 'offsetTop') + campo.offsetHeight;
var left = calcularPosicion(campo, 'offsetLeft');
contenedorSugerencias.style.top = top + 'px';
contenedorSugerencias.style.left = left + 'px';
contenedorSugerencias.style.width = campo.offsetWidth + 'px';
contenedorSugerencias.style.display = 'block';
var htmlInterno = '';
for (var i = 0; i < elementos.length; i++) {
htmlInterno += '<div htmlinterno="" id="opcion_' + i + '" style="padding:4px;cursor:pointer;">';
htmlInterno += elementos[i] + '</div>';
}
contenedorSugerencias.innerHTML = htmlInterno;
window.indiceSeleccionado = -1;
window.totalOpciones = elementos.length;
window.textoOriginal = campo.value;
}
function ocultarContenedor() {
if (contenedorSugerencias) contenedorSugerencias.style.display = 'none';
}
function seleccionarOpcion(elemento) {
document.getElementById(identificadorCampo).value = elemento.innerHTML;
ejecutarCallback();
ocultarContenedor();
}
function resaltarOpcion(elemento) {
limpiarResaltados();
elemento.style.backgroundColor = '#f0f0f0';
window.indiceSeleccionado = parseInt(elemento.id.replace('opcion_', ''), 10);
}
function quitarResaltado(elemento) {
elemento.style.backgroundColor = '';
}
function limpiarResaltados() {
for (var i = 0; i < window.totalOpciones; i++) {
var el = document.getElementById('opcion_' + i);
if (el) el.style.backgroundColor = '';
}
}
function navegarSiguiente() {
limpiarResaltados();
window.indiceSeleccionado++;
if (window.indiceSeleccionado >= window.totalOpciones) window.indiceSeleccionado = -1;
if (window.indiceSeleccionado === -1) {
document.getElementById(identificadorCampo).value = window.textoOriginal;
} else {
var opcion = document.getElementById('opcion_' + window.indiceSeleccionado);
opcion.style.backgroundColor = '#f0f0f0';
document.getElementById(identificadorCampo).value = opcion.innerHTML;
}
}
function navegarAnterior() {
limpiarResaltados();
if (window.indiceSeleccionado === -1) {
window.indiceSeleccionado = window.totalOpciones;
}
window.indiceSeleccionado--;
if (window.indiceSeleccionado === -1) {
document.getElementById(identificadorCampo).value = window.textoOriginal;
} else {
var opcion = document.getElementById('opcion_' + window.indiceSeleccionado);
opcion.style.backgroundColor = '#f0f0f0';
document.getElementById(identificadorCampo).value = opcion.innerHTML;
}
}
function ejecutarCallback() {
if (typeof funcionCallback === 'function') funcionCallback();
}
Ejemplo de uso en HTML
<html>
<head>
<title>Demo Autocompletado</title>
<script src="autocompletado.js"></script>
<script>
window.onload = function() {
inicializar('campoBusqueda', 'servidor.ashx', alSeleccionar);
}
function alSeleccionar() {
var valor = document.getElementById('campoBusqueda').value;
console.log('Seleccionado:', valor);
}
</script>
</head>
<body>
<label for="campoBusqueda">Término:</label>
<input type="text" id="campoBusqueda" />
<button onclick="alSeleccionar()">Buscar</button>
</body>
</html>
Archivo de servidor ASP.NET (servidor.ashx)
<%@ WebHandler Language="C#" Class="servidor" %>
using System;
using System.Web;
using System.IO;
public class servidor : IHttpHandler {
public void ProcessRequest(HttpContext contexto) {
contexto.Response.ContentType = "text/plain";
using (var lector = new StreamReader(contexto.Request.InputStream)) {
string consulta = lector.ReadToEnd();
// Simular datos de respuesta separados por comas
string[] datos = { consulta + "A", "Opción2", "Sugerencia3", "Ejemplo4" };
contexto.Response.Write(string.Join(",", datos));
}
}
public bool IsReusable {
get { return false; }
}
}