Implementación de Autocompletado en JavaScript con Soporte para Navegadores Antiguos

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; }
    }
}

Etiquetas: JavaScript AJAX autocompletado Internet Explorer ASP.NET

Publicado el 7-1 22:27